/* eslint-disable max-lines */
/* globals tryTests */
import * as stream from '@openpgp/web-stream-tools';
import { use as chaiUse, expect } from 'chai';
import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import
chaiUse(chaiAsPromised);

import openpgp from '../initOpenpgp.js';

import util from '../../src/util.js';

export default () => describe('Signature', function() {
  const priv_key_arm1 =
    ['-----BEGIN PGP PRIVATE KEY BLOCK-----',
      'Version: GnuPG v1.4.11 (GNU/Linux)',
      '',
      'lQHhBFERnrMRBADmM0hIfkI3yosjgbWo9v0Lnr3CCE+8KsMszgVS+hBu0XfGraKm',
      'ivcA2aaJimHqVYOP7gEnwFAxHBBpeTJcu5wzCFyJwEYqVeS3nnaIhBPplSF14Duf',
      'i6bB9RV7KxVAg6aunmM2tAutqC+a0y2rDaf7jkJoZ9gWJe2zI+vraD6fiwCgxvHo',
      '3IgULB9RqIqpLoMgXfcjC+cD/1jeJlKRm+n71ryYwT/ECKsspFz7S36z6q3XyS8Q',
      'QfrsUz2p1fbFicvJwIOJ8B20J/N2/nit4P0gBUTUxv3QEa7XCM/56/xrGkyBzscW',
      'AzBoy/AK9K7GN6z13RozuAS60F1xO7MQc6Yi2VU3eASDQEKiyL/Ubf/s/rkZ+sGj',
      'yJizBACtwCbQzA+z9XBZNUat5NPgcZz5Qeh1nwF9Nxnr6pyBv7tkrLh/3gxRGHqG',
      '063dMbUk8pmUcJzBUyRsNiIPDoEUsLjY5zmZZmp/waAhpREsnK29WLCbqLdpUors',
      'c1JJBsObkA1IM8TZY8YUmvsMEvBLCCanuKpclZZXqeRAeOHJ0v4DAwK8WfuTe5B+',
      'M2BOOeZbN8BpfiA1l//fMMHLRS3UvbLBv4P1+4SyvhyYTR7M76Q0xPc03MFOWHL+',
      'S9VumbQWVGVzdDIgPHRlc3QyQHRlc3QuY29tPohiBBMRAgAiBQJREZ6zAhsDBgsJ',
      'CAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRARJ5QDyxae+MXNAKCzWSDR3tMrTrDb',
      'TAri73N1Xb3j1ACfSl9y+SAah2q7GvmiR1+6+/ekqJGdAVgEURGesxAEANlpMZjW',
      '33jMxlKHDdyRFXtKOq8RreXhq00plorHbgz9zFEWm4VF53+E/KGnmHGyY5Cy8TKy',
      'ZjaueZZ9XuG0huZg5If68irFfNZtxdA26jv8//PdZ0Uj+X6J3RVa2peMLDDswTYL',
      'OL1ZO1fxdtDD40fdAiIZ1QyjwEG0APtz41EfAAMFBAC5/dtgBBPtHe8UjDBaUe4n',
      'NzHuUBBp6XE+H7eqHNFCuZAJ7yqJLGVHNIaQR419cNy08/OO/+YUQ7rg78LxjFiv',
      'CH7IzhfU+6yvELSbgRMicY6EnAP2GT+b1+MtFNa3lBGtBHcJla52c2rTAHthYZWk',
      'fT5R5DnJuQ2cJHBMS9HWyP4DAwK8WfuTe5B+M2C7a/YJSUv6SexdGCaiaTcAm6g/',
      'PvA6hw/FLzIEP67QcQSSTmhftQIwnddt4S4MyJJH3U4fJaFfYQ1zCniYJohJBBgR',
      'AgAJBQJREZ6zAhsMAAoJEBEnlAPLFp74QbMAn3V4857xwnO9/+vzIVnL93W3k0/8',
      'AKC8omYPPomN1E/UJFfXdLDIMi5LoA==',
      '=LSrW',
      '-----END PGP PRIVATE KEY BLOCK-----'].join('\n');

  const pub_key_arm1 =
    ['-----BEGIN PGP PUBLIC KEY BLOCK-----',
      'Version: GnuPG v1.4.11 (GNU/Linux)',
      '',
      'mQGiBFERlw4RBAD6Bmcf2w1dtUmtCLkdxeqZLArk3vYoQAjdibxA3gXVyur7fsWb',
      'ro0jVbBHqOCtC6jDxE2l52NP9+tTlWeVMaqqNvUE47LSaPq2DGI8Wx1Rj6bF3mTs',
      'obYEwhGbGh/MhJnME9AHODarvk8AZbzo0+k1EwrBWF6dTUBPfqO7rGU2ewCg80WV',
      'x5pt3evj8rRK3jQ8SMKTNRsD/1PhTdxdZTdXARAFzcW1VaaruWW0Rr1+XHKKwDCz',
      'i7HE76SO9qjnQfZCZG75CdQxI0h8GFeN3zsDqmhob2iSz2aJ1krtjM+iZ1FBFd57',
      'OqCV6wmk5IT0RBN12ZzMS19YvzN/ONXHrmTZlKExd9Mh9RKLeVNw+bf6JsKQEzcY',
      'JzFkBACX9X+hDYchO/2hiTwx4iOO9Fhsuh7eIWumB3gt+aUpm1jrSbas/QLTymmk',
      'uZuQVXI4NtnlvzlNgWv4L5s5RU5WqNGG7WSaKNdcrvJZRC2dgbUJt04J5CKrWp6R',
      'aIYal/81Ut1778lU01PEt563TcQnUBlnjU5OR25KhfSeN5CZY7QUVGVzdCA8dGVz',
      'dEB0ZXN0LmNvbT6IYgQTEQIAIgUCURGXDgIbAwYLCQgHAwIGFQgCCQoLBBYCAwEC',
      'HgECF4AACgkQikDlZK/UvLSspgCfcNaOpTg1W2ucR1JwBbBGvaERfuMAnRgt3/rs',
      'EplqEakMckCtikEnpxYe',
      '=b2Ln',
      '-----END PGP PUBLIC KEY BLOCK-----'].join('\n');

  const msg_arm1 =
    ['-----BEGIN PGP MESSAGE-----',
      'Version: GnuPG v1.4.11 (GNU/Linux)',
      '',
      'hQEOA1N4OCSSjECBEAP/diDJCQn4e88193PgqhbfAkohk9RQ0v0MPnXpJbCRTHKO',
      '8r9nxiAr/TQv4ZOingXdAp2JZEoE9pXxZ3r1UWew04czxmgJ8FP1ztZYWVFAWFVi',
      'Tj930TBD7L1fY/MD4fK6xjEG7z5GT8k4tn4mLm/PpWMbarIglfMopTy1M/py2cID',
      '/2Sj7Ikh3UFiG+zm4sViYc5roNbMy8ixeoKixxi99Mx8INa2cxNfqbabjblFyc0Z',
      'BwmbIc+ZiY2meRNI5y/tk0gRD7hT84IXGGl6/mH00bsX/kkWdKGeTvz8s5G8RDHa',
      'Za4HgLbXItkX/QarvRS9kvkD01ujHfj+1ZvgmOBttNfP0p8BQLIICqvg1eYD9aPB',
      '+GtOZ2F3+k5VyBL5yIn/s65SBjNO8Fqs3aL0x+p7s1cfUzx8J8a8nWpqq/qIQIqg',
      'ZJH6MZRKuQwscwH6NWgsSVwcnVCAXnYOpbHxFQ+j7RbF/+uiuqU+DFH/Rd5pik8b',
      '0Dqnp0yfefrkjQ0nuvubgB6Rv89mHpnvuJfFJRInpg4lrHwLvRwdpN2HDozFHcKK',
      'aOU=',
      '=4iGt',
      '-----END PGP MESSAGE-----'].join('\n');

  const priv_key_arm2 =
    ['-----BEGIN PGP PRIVATE KEY BLOCK-----',
      'Version: GnuPG v2.0.19 (GNU/Linux)',
      '',
      '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',
      '=lw5e',
      '-----END PGP PRIVATE KEY BLOCK-----'].join('\n');

  const pub_key_arm2 =
    ['-----BEGIN PGP PUBLIC KEY BLOCK-----',
      'Version: GnuPG v2.0.19 (GNU/Linux)',
      '',
      'mI0EUmEvTgEEANyWtQQMOybQ9JltDqmaX0WnNPJeLILIM36sw6zL0nfTQ5zXSS3+',
      'fIF6P29lJFxpblWk02PSID5zX/DYU9/zjM2xPO8Oa4xo0cVTOTLj++Ri5mtr//f5',
      'GLsIXxFrBJhD/ghFsL3Op0GXOeLJ9A5bsOn8th7x6JucNKuaRB6bQbSPABEBAAG0',
      'JFRlc3QgTWNUZXN0aW5ndG9uIDx0ZXN0QGV4YW1wbGUuY29tPoi5BBMBAgAjBQJS',
      'YS9OAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQSmNhOk1uQJQwDAP6',
      'AgrTyqkRlJVqz2pb46TfbDM2TDF7o9CBnBzIGoxBhlRwpqALz7z2kxBDmwpQa+ki',
      'Bq3jZN/UosY9y8bhwMAlnrDY9jP1gdCo+H0sD48CdXybblNwaYpwqC8VSpDdTndf',
      '9j2wE/weihGp/DAdy/2kyBCaiOY1sjhUfJ1GogF49rC4jQRSYS9OAQQA6R/PtBFa',
      'JaT4jq10yqASk4sqwVMsc6HcifM5lSdxzExFP74naUMMyEsKHP53QxTF0Grqusag',
      'Qg/ZtgT0CN1HUM152y7ACOdp1giKjpMzOTQClqCoclyvWOFB+L/SwGEIJf7LSCEr',
      'woBuJifJc8xAVr0XX0JthoW+uP91eTQ3XpsAEQEAAYkBPQQYAQIACQUCUmEvTgIb',
      'LgCoCRBKY2E6TW5AlJ0gBBkBAgAGBQJSYS9OAAoJEOCE90RsICyXuqIEANmmiRCA',
      'SF7YK7PvFkieJNwzeK0V3F2lGX+uu6Y3Q/Zxdtwc4xR+me/CSBmsURyXTO29OWhP',
      'GLszPH9zSJU9BdDi6v0yNprmFPX/1Ng0Abn/sCkwetvjxC1YIvTLFwtUL/7v6NS2',
      'bZpsUxRTg9+cSrMWWSNjiY9qUKajm1tuzPDZXAUEAMNmAN3xXN/Kjyvj2OK2ck0X',
      'W748sl/tc3qiKPMJ+0AkMF7Pjhmh9nxqE9+QCEl7qinFqqBLjuzgUhBU4QlwX1GD',
      'AtNTq6ihLMD5v1d82ZC7tNatdlDMGWnIdvEMCv2GZcuIqDQ9rXWs49e7tq1NncLY',
      'hz3tYjKhoFTKEIq3y3Pp',
      '=h/aX',
      '-----END PGP PUBLIC KEY BLOCK-----'].join('\n');

  const pub_key_arm3 =
    ['-----BEGIN PGP PUBLIC KEY BLOCK-----',
      'Version: GnuPG v2.0.19 (GNU/Linux)',
      '',
      'mQENBFKV0FUBCACtZliApy01KBGbGNB36YGH4lpr+5KoqF1I8A5IT0YeNjyGisOk',
      'WsDsUzOqaNvgzQ82I3MY/jQV5rLBhH/6LiRmCA16WkKcqBrHfNGIxJ+Q+ofVBHUb',
      'aS9ClXYI88j747QgWzirnLuEA0GfilRZcewII1pDA/G7+m1HwV4qHsPataYLeboq',
      'hPA3h1EVVQFMAcwlqjOuS8+weHQRfNVRGQdRMm6H7166PseDVRUHdkJpVaKFhptg',
      'rDoNI0lO+UujdqeF1o5tVZ0j/s7RbyBvdLTXNuBbcpq93ceSWuJPZmi1XztQXKYe',
      'y0f+ltgVtZDEc7TGV5WDX9erRECCcA3+s7J3ABEBAAG0G0pTIENyeXB0byA8ZGlm',
      'ZmllQGhvbWUub3JnPokBPwQTAQIAKQUCUpXQVQIbAwUJCWYBgAcLCQgHAwIBBhUI',
      'AgkKCwQWAgMBAh4BAheAAAoJENvyI+hwU030yRAIAKX/mGEgi/miqasbbQoyK/CS',
      'a7sRxgZwOWQLdi2xxpE5V4W4HJIDNLJs5vGpRN4mmcNK2fmJAh74w0PskmVgJEhP',
      'dFJ14UC3fFPq5nbqkBl7hU0tDP5jZxo9ruQZfDOWpHKxOCz5guYJ0CW97bz4fChZ',
      'NFDyfU7VsJQwRIoViVcMCipP0fVZQkIhhwpzQpmVmN8E0a6jWezTZv1YpMdlzbEf',
      'H79l3StaOh9/Un9CkIyqEWdYiKvIYms9nENyehN7r/OKYN3SW+qlt5GaL+ws+N1w',
      '6kEZjPFwnsr+Y4A3oHcAwXq7nfOz71USojSmmo8pgdN8je16CP98vw3/k6TncLS5',
      'AQ0EUpXQVQEIAMEjHMeqg7B04FliUFWr/8C6sJDb492MlGAWgghIbnuJfXAnUGdN',
      'oAzn0S+n93Y/qHbW6YcjHD4/G+kK3MuxthAFqcVjdHZQXK0rkhXO/u1co7v1cdtk',
      'OTEcyOpyLXolM/1S2UYImhrml7YulTHMnWVja7xu6QIRso+7HBFT/u9D47L/xXrX',
      'MzXFVZfBtVY+yoeTrOY3OX9cBMOAu0kuN9eT18Yv2yi6XMzP3iONVHtl6HfFrAA7',
      'kAtx4ne0jgAPWZ+a8hMy59on2ZFs/AvSpJtSc1kw/vMTWkyVP1Ky20vAPHQ6Ej5q',
      '1NGJ/JbcFgolvEeI/3uDueLjj4SdSIbLOXMAEQEAAYkBJQQYAQIADwUCUpXQVQIb',
      'DAUJCWYBgAAKCRDb8iPocFNN9NLkB/wO4iRxia0zf4Kw2RLVZG8qcuo3Bw9UTXYY',
      'lI0AutoLNnSURMLLCq6rcJ0BCXGj/2iZ0NBxZq3t5vbRh6uUv+hpiSxK1nF7AheN',
      '4aAAzhbWx0UDTF04ebG/neE4uDklRIJLhif6+Bwu+EUeTlGbDj7fqGSsNe8g92w7',
      '1e41rF/9CMoOswrKgIjXAou3aexogWcHvKY2D+1q9exORe1rIa1+sUGl5PG2wsEs',
      'znN6qtN5gMlGY1ofWDY+I02gO4qzaZ/FxRZfittCw7v5dmQYKot9qRi2Kx3Fvw+h',
      'ivFBpC4TWgppFBnJJnAsFXZJQcejMW4nEmOViRQXY8N8PepQmgsu',
      '=ummy',
      '-----END PGP PUBLIC KEY BLOCK-----'].join('\n');

  const pub_revoked =
    ['-----BEGIN PGP PUBLIC KEY BLOCK-----',
      'Version: GnuPG v2.0.19 (GNU/Linux)',
      '',
      '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',
      'pMiJM+NJAQ==',
      '=ok+o',
      '-----END PGP PUBLIC KEY BLOCK-----'].join('\n');

  const pub_latin1_msg = `-----BEGIN PGP PUBLIC KEY BLOCK-----

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
eGlmAABNTQAqAAAACAAFARIAAwAAAAEAAQAAARoABQAAAAEAAABKARsABQAAAAEA
AABSASgAAwAAAAEAAgAAh2kABAAAAAEAAABaAAAAAAAAAEgAAAABAAAASAAAAAEA
AqACAAQAAAABAAAAfaADAAQAAAABAAAAlgAAAAD/4QkhaHR0cDovL25zLmFkb2Jl
LmNvbS94YXAvMS4wLwA8P3hwYWNrZXQgYmVnaW49Iu+7vyIgaWQ9Ilc1TTBNcENl
aGlIenJlU3pOVGN6a2M5ZCI/PiA8eDp4bXBtZXRhIHhtbG5zOng9ImFkb2JlOm5z
Om1ldGEvIiB4OnhtcHRrPSJYTVAgQ29yZSA1LjQuMCI+IDxyZGY6UkRGIHhtbG5z
OnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5z
IyI+IDxyZGY6RGVzY3JpcHRpb24gcmRmOmFib3V0PSIiLz4gPC9yZGY6UkRGPiA8
L3g6eG1wbWV0YT4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg
ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8P3hw
YWNrZXQgZW5kPSJ3Ij8+AP/tADhQaG90b3Nob3AgMy4wADhCSU0EBAAAAAAAADhC
SU0EJQAAAAAAENQdjNmPALIE6YAJmOz4Qn7/4gzgSUNDX1BST0ZJTEUAAQEAAAzQ
YXBwbAIQAABtbnRyUkdCIFhZWiAH3wABAAEACwA7AC1hY3NwQVBQTAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAAAADTLWFwcGwAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABFkZXNjAAABUAAAAGJkc2Nt
AAABtAAAAbhjcHJ0AAADbAAAACN3dHB0AAADkAAAABRyWFlaAAADpAAAABRnWFla
AAADuAAAABRiWFlaAAADzAAAABRyVFJDAAAD4AAACAxhYXJnAAAL7AAAACB2Y2d0
AAAMDAAAADBuZGluAAAMPAAAAD5jaGFkAAAMfAAAACxtbW9kAAAMqAAAAChiVFJD
AAAD4AAACAxnVFJDAAAD4AAACAxhYWJnAAAL7AAAACBhYWdnAAAL7AAAACBkZXNj
AAAAAAAAAAhEaXNwbGF5AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
bWx1YwAAAAAAAAAiAAAADGhySFIAAAAQAAABqGtvS1IAAAAQAAABqG5iTk8AAAAQ
AAABqGlkAAAAAAAQAAABqGh1SFUAAAAQAAABqGNzQ1oAAAAQAAABqGRhREsAAAAQ
AAABqHVrVUEAAAAQAAABqGFyAAAAAAAQAAABqGl0SVQAAAAQAAABqHJvUk8AAAAQ
AAABqGVzRVMAAAAQAAABqGhlSUwAAAAQAAABqG5sTkwAAAAQAAABqGZpRkkAAAAQ
AAABqHpoVFcAAAAQAAABqHZpVk4AAAAQAAABqHNrU0sAAAAQAAABqHpoQ04AAAAQ
AAABqHJ1UlUAAAAQAAABqGZyRlIAAAAQAAABqG1zAAAAAAAQAAABqGNhRVMAAAAQ
AAABqHRoVEgAAAAQAAABqGVzWEwAAAAQAAABqGRlREUAAAAQAAABqGVuVVMAAAAQ
AAABqHB0QlIAAAAQAAABqHBsUEwAAAAQAAABqGVsR1IAAAAQAAABqHN2U0UAAAAQ
AAABqHRyVFIAAAAQAAABqGphSlAAAAAQAAABqHB0UFQAAAAQAAABqABTAE0AQgAx
ADkANAAwAFd0ZXh0AAAAAENvcHlyaWdodCBBcHBsZSBJbmMuLCAyMDE1AABYWVog
AAAAAAAA89gAAQAAAAEWCFhZWiAAAAAAAABwAAAAOREAAAPCWFlaIAAAAAAAAGI3
AAC3mgAAGRFYWVogAAAAAAAAJJ8AAA9VAAC2WmN1cnYAAAAAAAAEAAAAAAUACgAP
ABQAGQAeACMAKAAtADIANgA7AEAARQBKAE8AVABZAF4AYwBoAG0AcgB3AHwAgQCG
AIsAkACVAJoAnwCjAKgArQCyALcAvADBAMYAywDQANUA2wDgAOUA6wDwAPYA+wEB
AQcBDQETARkBHwElASsBMgE4AT4BRQFMAVIBWQFgAWcBbgF1AXwBgwGLAZIBmgGh
AakBsQG5AcEByQHRAdkB4QHpAfIB+gIDAgwCFAIdAiYCLwI4AkECSwJUAl0CZwJx
AnoChAKOApgCogKsArYCwQLLAtUC4ALrAvUDAAMLAxYDIQMtAzgDQwNPA1oDZgNy
A34DigOWA6IDrgO6A8cD0wPgA+wD+QQGBBMEIAQtBDsESARVBGMEcQR+BIwEmgSo
BLYExATTBOEE8AT+BQ0FHAUrBToFSQVYBWcFdwWGBZYFpgW1BcUF1QXlBfYGBgYW
BicGNwZIBlkGagZ7BowGnQavBsAG0QbjBvUHBwcZBysHPQdPB2EHdAeGB5kHrAe/
B9IH5Qf4CAsIHwgyCEYIWghuCIIIlgiqCL4I0gjnCPsJEAklCToJTwlkCXkJjwmk
CboJzwnlCfsKEQonCj0KVApqCoEKmAquCsUK3ArzCwsLIgs5C1ELaQuAC5gLsAvI
C+EL+QwSDCoMQwxcDHUMjgynDMAM2QzzDQ0NJg1ADVoNdA2ODakNww3eDfgOEw4u
DkkOZA5/DpsOtg7SDu4PCQ8lD0EPXg96D5YPsw/PD+wQCRAmEEMQYRB+EJsQuRDX
EPURExExEU8RbRGMEaoRyRHoEgcSJhJFEmQShBKjEsMS4xMDEyMTQxNjE4MTpBPF
E+UUBhQnFEkUahSLFK0UzhTwFRIVNBVWFXgVmxW9FeAWAxYmFkkWbBaPFrIW1hb6
Fx0XQRdlF4kXrhfSF/cYGxhAGGUYihivGNUY+hkgGUUZaxmRGbcZ3RoEGioaURp3
Gp4axRrsGxQbOxtjG4obshvaHAIcKhxSHHscoxzMHPUdHh1HHXAdmR3DHeweFh5A
HmoelB6+HukfEx8+H2kflB+/H+ogFSBBIGwgmCDEIPAhHCFIIXUhoSHOIfsiJyJV
IoIiryLdIwojOCNmI5QjwiPwJB8kTSR8JKsk2iUJJTglaCWXJccl9yYnJlcmhya3
JugnGCdJJ3onqyfcKA0oPyhxKKIo1CkGKTgpaymdKdAqAio1KmgqmyrPKwIrNitp
K50r0SwFLDksbiyiLNctDC1BLXYtqy3hLhYuTC6CLrcu7i8kL1ovkS/HL/4wNTBs
MKQw2zESMUoxgjG6MfIyKjJjMpsy1DMNM0YzfzO4M/E0KzRlNJ402DUTNU01hzXC
Nf02NzZyNq426TckN2A3nDfXOBQ4UDiMOMg5BTlCOX85vDn5OjY6dDqyOu87LTtr
O6o76DwnPGU8pDzjPSI9YT2hPeA+ID5gPqA+4D8hP2E/oj/iQCNAZECmQOdBKUFq
QaxB7kIwQnJCtUL3QzpDfUPARANER0SKRM5FEkVVRZpF3kYiRmdGq0bwRzVHe0fA
SAVIS0iRSNdJHUljSalJ8Eo3Sn1KxEsMS1NLmkviTCpMcky6TQJNSk2TTdxOJU5u
TrdPAE9JT5NP3VAnUHFQu1EGUVBRm1HmUjFSfFLHUxNTX1OqU/ZUQlSPVNtVKFV1
VcJWD1ZcVqlW91dEV5JX4FgvWH1Yy1kaWWlZuFoHWlZaplr1W0VblVvlXDVchlzW
XSddeF3JXhpebF69Xw9fYV+zYAVgV2CqYPxhT2GiYfViSWKcYvBjQ2OXY+tkQGSU
ZOllPWWSZedmPWaSZuhnPWeTZ+loP2iWaOxpQ2maafFqSGqfavdrT2una/9sV2yv
bQhtYG25bhJua27Ebx5veG/RcCtwhnDgcTpxlXHwcktypnMBc11zuHQUdHB0zHUo
dYV14XY+dpt2+HdWd7N4EXhueMx5KnmJeed6RnqlewR7Y3vCfCF8gXzhfUF9oX4B
fmJ+wn8jf4R/5YBHgKiBCoFrgc2CMIKSgvSDV4O6hB2EgITjhUeFq4YOhnKG14c7
h5+IBIhpiM6JM4mZif6KZIrKizCLlov8jGOMyo0xjZiN/45mjs6PNo+ekAaQbpDW
kT+RqJIRknqS45NNk7aUIJSKlPSVX5XJljSWn5cKl3WX4JhMmLiZJJmQmfyaaJrV
m0Kbr5wcnImc951kndKeQJ6unx2fi5/6oGmg2KFHobaiJqKWowajdqPmpFakx6U4
pammGqaLpv2nbqfgqFKoxKk3qamqHKqPqwKrdavprFys0K1ErbiuLa6hrxavi7AA
sHWw6rFgsdayS7LCszizrrQltJy1E7WKtgG2ebbwt2i34LhZuNG5SrnCuju6tbsu
u6e8IbybvRW9j74KvoS+/796v/XAcMDswWfB48JfwtvDWMPUxFHEzsVLxcjGRsbD
x0HHv8g9yLzJOsm5yjjKt8s2y7bMNcy1zTXNtc42zrbPN8+40DnQutE80b7SP9LB
00TTxtRJ1MvVTtXR1lXW2Ndc1+DYZNjo2WzZ8dp22vvbgNwF3IrdEN2W3hzeot8p
36/gNuC94UThzOJT4tvjY+Pr5HPk/OWE5g3mlucf56noMui86Ubp0Opb6uXrcOv7
7IbtEe2c7ijutO9A78zwWPDl8XLx//KM8xnzp/Q09ML1UPXe9m32+/eK+Bn4qPk4
+cf6V/rn+3f8B/yY/Sn9uv5L/tz/bf//cGFyYQAAAAAAAwAAAAJmZgAA8qcAAA1Z
AAAT0AAACg52Y2d0AAAAAAAAAAEAAQAAAAAAAAABAAAAAQAAAAAAAAABAAAAAQAA
AAAAAAABAABuZGluAAAAAAAAADYAAKPAAABUQAAATMAAAJmAAAAmgAAAD0AAAFBA
AABUQAACMzMAAjMzAAIzMwAAAAAAAAAAc2YzMgAAAAAAAQu3AAAFlv//81cAAAcp
AAD91///+7f///2mAAAD2gAAwPZtbW9kAAAAAAAATC0AAAaVVjg5RchujAAAAAAA
AAAAAAAAAAAAAAAA/8AAEQgAlgB9AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAA
AAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFB
BhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNE
RUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqi
o6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz
9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIB
AgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy
0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpz
dHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG
x8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAgICAgICAwICAwQD
AwMEBQQEBAQFBwUFBQUFBwgHBwcHBwcICAgICAgICAoKCgoKCgsLCwsLDQ0NDQ0N
DQ0NDf/bAEMBAgICAwMDBgMDBg0JBwkNDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0N
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
Blk+CJ7ytHy6En8542bB/yC+Z9/zWbVuhg==
=jmT1
-----END PGP PUBLIC KEY BLOCK-----`;

  const keyExpiredBindingSig = `-----BEGIN PGP PUBLIC KEY BLOCK-----

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
hW1Hj9AO8lzggBQ=
=Nt+N
-----END PGP PUBLIC KEY BLOCK-----`;

  const sequoiaBobPublicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: Bob's OpenPGP certificate

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
NEJd3XZRzaXZE2aAMQ==
=NXei
-----END PGP PUBLIC KEY BLOCK-----`;

  const signature_with_critical_notation = `-----BEGIN PGP MESSAGE-----

owGbwMvMwMH4oOW7S46CznTG09xJDDE3Wl1KUotLuDousDAwcjBYiSmyXL+48d6x
U1PSGUxcj8IUszKBVMpMaWAAAgEGZpAeh9SKxNyCnFS95PzcytRiBi5OAZjyXXzM
f8WYLqv7TXP61Sa4rqT12CI3xaN73YS2pt089f96odCKaEPnWJ3iSGmzJaW/ug10
2Zo8Wj2k4s7t8wt4H3HtTu+y5UZfV3VOO+l//sdE/o+Lsub8FZH7/eOq7OnbNp4n
vwjE8mqJXetNMfj8r2SCyvkEnlVRYR+/mnge+ib56FdJ8uKtqSxyvgA=
=fRXs
-----END PGP MESSAGE-----`;

  const signature_with_non_human_readable_notations = `-----BEGIN PGP SIGNATURE-----

wncEARYKAB8FAl2TS9MYFAAAAAAADAADdGVzdEBrZXkuY29tAQIDAAoJEGZ9
gtV/iL8hrhMBAOQ/UgqRTbx1Z8inGmRdUx1cJU1SR4Pnq/eJNH/CFk5DAP0Q
hUhMKMuiM3pRwdIyDOItkUWQmjEEw7/XmhgInkXsCw==
=ZGXr
-----END PGP SIGNATURE-----
`;

  it('Retrieve the issuer Key ID of a signature', async function () {
    const publicKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const privateKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });
    const message = await openpgp.createMessage({ text: 'test' });
    const armoredSignature = await openpgp.sign({
      message,
      signingKeys: privateKey,
      signingKeyIDs: privateKey.getKeyID(),
      detached: true,
      config: { minRSABits: 1024 }
    });
    const signature = await openpgp.readSignature({ armoredSignature });
    expect(signature.getSigningKeyIDs()).to.exist;
    expect(signature.getSigningKeyIDs().map(x => x.toHex())).to.include(publicKey.getKeyID().toHex());
  });

  it('Generates valid one-pass signature packets (v6 keys)', async function () {
    const v6PrivateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
k0mXubZvyl4GBg==
-----END PGP PRIVATE KEY BLOCK-----` });
    const v4PrivateKey = await openpgp.readKey({
      armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEZOjwQBYJKwYBBAHaRw8BAQdAIzsYHb7T7NhSFmkWKSk5ItaBYv2HET7u
IFXhGdvOpogAAQDMk8SQlysNkLe7VRwXedX63St1a2V6/dzDG926oPyW3hJ6
zQ48dGVzdEB0ZXN0Lml0PsKMBBAWCgA+BYJk6PBABAsJBwgJkFMSwQwprjrH
AxUICgQWAAIBAhkBApsDAh4BFiEEwKKS1V/wldBNfwpjUxLBDCmuOscAAONc
AQDU1gi9moPtQzh6rrpKPWZ8nMSiZzLb3LYmpqz9Tn9YFAEAmEC2uHET/Oj6
VVxvMwvGytr5y0nuc6ZV5D0I2qoMcAHHXQRk6PBAEgorBgEEAZdVAQUBAQdA
xeBFSxzKOtLEQ6mVO2mnxvDiiMKmq7NbOezoWkbeSjgDAQgHAAD/YMvH+eDP
h1RplHOiaAMwhpTRGSGOaC6+pu6AMWiRLWAQ88J4BBgWCAAqBYJk6PBACZBT
EsEMKa46xwKbDBYhBMCiktVf8JXQTX8KY1MSwQwprjrHAAD4igEAzoExR6VX
EY9xxFKtAVdtYOT61d0FZibs0TD0VjbqzdkBAK60jT9jKefpnZ9sGv7bhSRX
2hrIPyCF2f0R5Js3LCML
=xyQ6
-----END PGP PRIVATE KEY BLOCK-----`
    });
    const armoredSignedMessage = await openpgp.sign({
      message: await openpgp.createMessage({ text: 'test' }),
      signingKeys: [v6PrivateKey, v4PrivateKey, v6PrivateKey] // get multiple signature packets
    });
    const signedMessage = await openpgp.readMessage({ armoredMessage: armoredSignedMessage });
    // read signature packet stream
    signedMessage.packets.push(...await stream.readToEnd(signedMessage.packets.stream, _ => _));
    const signature1 = signedMessage.packets[4];
    const signature2 = signedMessage.packets[5]; // v4 sig
    const signature3 = signedMessage.packets[6];
    const opsSignature1 = signedMessage.packets[2];
    const opsSignature2 = signedMessage.packets[1];
    const opsSignature3 = signedMessage.packets[0];
    expect(opsSignature1).to.be.instanceOf(openpgp.OnePassSignaturePacket);
    expect(signature1).to.be.instanceOf(openpgp.SignaturePacket);
    expect(opsSignature2).to.be.instanceOf(openpgp.OnePassSignaturePacket);
    expect(signature2).to.be.instanceOf(openpgp.SignaturePacket);
    expect(opsSignature3).to.be.instanceOf(openpgp.OnePassSignaturePacket);
    expect(signature3).to.be.instanceOf(openpgp.SignaturePacket);
    expect(opsSignature1.version).to.equal(6);
    expect(opsSignature2.version).to.equal(3);
    expect(opsSignature3.version).to.equal(6);
    expect(util.uint8ArrayToHex(opsSignature1.issuerFingerprint)).to.equal(v6PrivateKey.getFingerprint());
    expect(util.uint8ArrayToHex(opsSignature3.issuerFingerprint)).to.equal(v6PrivateKey.getFingerprint());
    expect(opsSignature1.salt).to.deep.equal(signature1.salt);
    expect(opsSignature2.salt).to.be.null;
    expect(opsSignature3.salt).to.deep.equal(signature3.salt);
    expect(opsSignature1.salt).to.not.deep.equal(opsSignature3.salt); // sanity check
  });

  it('Throws when reading a signature missing the creation time', async function () {
    const armoredSignature = `-----BEGIN PGP SIGNATURE-----

wsDtBAABCAAXFiEE0aZuGiOxgsmYD3iM+/zIKgFeczAACgkQ+/zIKgFeczDjiwv+
LFUWJohCYtauaVDHBDHWF+tojls+ducY6uuU6iUTBb1969okh2sjUmvPwIjrVXuk
cfPl616xRqVWolEU9T5sG6MjRlAaG31Oo/FVAVFXZn30ldEtuDss12+/IhES3Wfx
M1jGdJZ1ZMZJpRsNBJXBegEBFKbPleEd66seuuFfvoIUbgsdj7IT/ZlEMlixelWW
igXPVY1C05oPbkC8oo0lVSxwdq6gDvm8a52k3GCtXJELrYGH29C+eDqmyLP1zJOt
NBoZBAqMd9XYVrJtuip436D9pdo5pbg4zCE6uPf2zzx4taK7jGkk6nn7LqVDxvQm
3dAXUnIxw4V9eL3V8SFAKwmouUmHPRbjfnQ70hxYQxDXUcIwu1aYn13QS1s/F/jf
DVRZWaAhNdL9BfHfhEsRVsmjMhe0zwRpaepvXnERbnA/lAUHEmEvgfPFz/2GsAo/
kCNcH9WI6idSzFjuYegECf+ZA1xOCjS9oLTGbSeT7jNfC8dH5+E92qlBLq4Ctt7k
=lMU7
-----END PGP SIGNATURE-----`;

    await expect(openpgp.readSignature({ armoredSignature })).to.be.rejectedWith(/Missing signature creation time/);
  });

  it('Supports parsing v6 signature with unknown hash algo', async function () {
    // v6 signatures must check that the salt size is correct.
    // but we want to support parsing signatures with unknown hash algos.
    // this test checks that the parsing succeeds, while signing/verification fails.

    // key with direct signature of unknown hash algo 99
    const armoredKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----

xUsGZN8edBsAAAAgdUMlFMFCVKNo7sdUd6FVBos6NNjpUpSdrodk6BfPb/kA
+3buA2+WY2LwyxlX5o07WR2VSn+wuegC3v28yO0tClHCtwYfG2MAAABIBYJk
3x50BAsJCAcHFQ4MCgkICwIWAAIXgAKbAwIeCSIhBpbSe0QWuaCNSSLaePhX
EP3BxQ2VHX3WpW1U6svHvCUiBScJAgcCAAAAACMZIP8aHixoyC9wS3q/TNV/
IfOQa81f+U5Ucz6H4I+c5bWRYUzH/piBB4n5FoYlld+/SViCQIBCQ+fynLma
j5wlf22+mISTt/9je1ZfYWlJ+WSJyi5gY5EH9DubfuIU3VaqCM0aQmVybmFk
ZXR0ZSA8YkBleGFtcGxlLm9yZz7CugYTGw4AAABLBYJk3x50BAsJCAcHFQ4M
CgkICwIWAAIXgAIZAQKbAwIeCSIhBpbSe0QWuaCNSSLaePhXEP3BxQ2VHX3W
pW1U6svHvCUiBScJAgcCAAAAAMMGIJEi9+yqkFKsNwX1H5II0riPudFpwBx2
ypVjNk4aNb7Exl56Aac4tXEhz4fH41q0dAzFww2erZaiUqmohQ4AFSw1jN/W
OiDfb1DkjT/HJ8vXMGpwWdgFPoqsWzTNhd5VCQ==
-----END PGP PRIVATE KEY BLOCK-----`;

    const key = await openpgp.readKey({ armoredKey });
    await expect(key.getSigningKey()).to.be.rejectedWith(/Unsupported hash function/);
  });

  it('Ignores marker packets when verifying signatures', async function () {
    const signatureWithMarkerPacket = `-----BEGIN PGP SIGNATURE-----

ygNQR1DCwPMEAAEKAAYFgl8831AAIQkQ+/zIKgFeczAWIQTRpm4aI7GCyZgPeIz7
/MgqAV5zMLckDACWiDbasKMTX/+czxHXyVcFJ/+ZeYqKEjYq6LueHy11XjJ0NZAM
LG9TqsXpWOsHrwE6wUQ7RvKQYtfIAeMUZtD87/XomIj6B/rQC5dHuQTb0b8lrRJb
OuW1sz6AYwceqkSvN3T5+KKNMXkaFw/DzWGPfqQQJDOqfgKxf5uO7GPVzaIU6aXn
76iKHQ7wowT2qHoFhd+t4S11iGr6XJef6QqIW2kTetZMf2Dp/rr7228VJJ1S0RdD
xxKJEbNrmdMNgE8/U+pkWjMQyVOOxWyPKlG3kv2Cu/naj4Lg2io3RhOAuNW5xEJF
gAId3WUNl3/PCu/PcTS1yS/Nj0ptwjKHwG0Zg8Dk5Jey8lUVyVhjxrV5tb6NLoAG
RlyTajZ3Sjhsg4mXHopjSF2w30saN64L5VAfGF1afFu7yzNYC+Fn6GL5yTJfKs4j
PNo4zCwOCumsVP0jWp09yUNflE6MTd21miBgbmPxyLyuwP2YrvT4+rCl+meNZ98a
cJRRGJPL16wINuk=
=VNoM
-----END PGP SIGNATURE-----`;

    const key = await openpgp.readKey({ armoredKey: sequoiaBobPublicKey });
    const message = await openpgp.createMessage({ text: 'Marker + Detached signature' });
    const signature = await openpgp.readSignature({ armoredSignature: signatureWithMarkerPacket });
    const { signatures: [sigInfo] } = await openpgp.verify({ message, signature, verificationKeys: key });
    expect(await sigInfo.verified).to.be.true;
  });


  it('Testing signature checking on CAST5-enciphered message', async function() {
    const publicKey = await openpgp.readKey({ armoredKey: pub_key_arm1 });
    const privateKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm1 }),
      passphrase: 'abcd'
    });
    const message = await openpgp.readMessage({ armoredMessage: msg_arm1 });
    const config = {
      rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]),
      rejectPublicKeyAlgorithms: new Set()
    };
    const { data, signatures } = await openpgp.decrypt({ decryptionKeys: privateKey, verificationKeys: publicKey, message, config });
    expect(data).to.exist;
    expect(await signatures[0].verified).to.be.true;
    expect((await signatures[0].signature).packets.length).to.equal(1);
  });

  it('Consider signature expired at the expiration time', async function() {
    const key = await openpgp.readKey({ armoredKey: keyExpiredBindingSig });
    const { embeddedSignature } = key.subkeys[0].bindingSignatures[0];
    expect(embeddedSignature.isExpired(embeddedSignature.created)).to.be.false;
    expect(embeddedSignature.isExpired(new Date(embeddedSignature.getExpirationTime() - 1))).to.be.false;
    expect(embeddedSignature.isExpired(embeddedSignature.getExpirationTime())).to.be.true;
  });

  it('Signing fails if primary key is expired', async function() {
    const armoredExpiredKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEYKKPDRYJKwYBBAHaRw8BAQdAwJcSQMkHVnZPesPJP1JaB9ptV+wG8Io1
vxRKvXQe0wMAAP0fdn6gvpVwFUE4bIRcn9hx6eDxSxUu+tg/t959Oo+iahF1
zRB0ZXN0IDx0ZXN0QGEuaXQ+wpIEEBYKACMFAmCijw0FCQAAAAEECwkHCAMV
CAoEFgACAQIZAQIbAwIeAQAhCRD16pevybCusRYhBHjm9svlAjmgVWL4wvXq
l6/JsK6xGUQBAPzxKS2Qs+vWGpxPT2N2T+PLHIgCOxVJVngj4fzREFH1AP9t
wP+fn3eSsik+vFGy93REmlD1xdu7nW/sHuxY4roqBcddBGCijw0SCisGAQQB
l1UBBQEBB0Cl1lr+aHfy6V4ePmZUULK6VKTCTPTMaPpR2TzKNIJQBQMBCAcA
AP9DZWRqQLCIkF38Q0UC/YXLCDdBEQdnlwpHgA0W1bSWmA3uwn4EGBYIAA8F
AmCijw0FCQAAAAECGwwAIQkQ9eqXr8mwrrEWIQR45vbL5QI5oFVi+ML16pev
ybCusYE4AQCYbXw8ZWoMevbOM7lAttkwyrG3V/nTW6BVo7/M9Pr9swEA0mDI
DQmhI0SZoTKy4EGhS0bNJ+g2+dJ8Y22fKzLWXwo=
=qiIN
-----END PGP PRIVATE KEY BLOCK-----`;
    const key = await openpgp.readKey({ armoredKey: armoredExpiredKey });
    await expect(openpgp.sign({
      signingKeys: key,
      message: await openpgp.createMessage({ text: 'Hello World' })
    })).to.be.rejectedWith(/key is expired/);
  });

  it('Signing fails if the signing date is before the key creation date', async function() {
    const key = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });
    await expect(openpgp.sign({
      signingKeys: key,
      date: new Date(key.keyPacket.created - 3600),
      message: await openpgp.createMessage({ text: 'Hello World' }),
      config: { minRSABits: 1024 }
    })).to.be.rejectedWith(/Signature creation time is in the future/);
  });

  it('Verification fails if primary key binding signature is expired', async function() {
    const armoredSignature = `-----BEGIN PGP SIGNATURE-----

wsDzBAABCgAGBYJfLAFsACEJEHwvqk35PDeyFiEEHdzhXwkhfO4vOzdgfC+qTfk8
N7KiqwwAts4QGB7v9bABCC2qkTxJhmStC0wQMcHRcjL/qAiVnmasQWmvE9KVsdm3
AaXd8mIx4a37/RRvr9dYrY2eE4uw72cMqPxNja2tvVXkHQvk1oEUqfkvbXs4ypKI
NyeTWjXNOTZEbg0hbm3nMy+Wv7zgB1CEvAsEboLDJlhGqPcD+X8a6CJGrBGUBUrv
KVmZr3U6vEzClz3DBLpoddCQseJRhT4YM1nKmBlZ5quh2LFgTSpajv5OsZheqt9y
EZAPbqmLhDmWRQwGzkWHKceKS7nZ/ox2WK6OS7Ob8ZGZkM64iPo6/EGj5Yc19vQN
AGiIaPEGszBBWlOpHTPhNm0LB0nMWqqaT87oNYwP8CQuuxDb6rKJ2lffCmZH27Lb
UbQZcH8J+0UhpeaiadPZxH5ATJAcenmVtVVMLVOFnm+eIlxzov9ntpgGYt8hLdXB
ITEG9mMgp3TGS9ZzSifMZ8UGtHdp9QdBg8NEVPFzDOMGxpc/Bftav7RRRuPiAER+
7A5CBid5
=aQkm
-----END PGP SIGNATURE-----`;
    const key = await openpgp.readKey({ armoredKey: keyExpiredBindingSig });
    const signature = await openpgp.readSignature({ armoredSignature });
    const { signatures: [sigInfo] } = await openpgp.verify({
      verificationKeys: key,
      message: await openpgp.createMessage({ text: 'Hello World :)' }),
      signature
    });
    await expect(sigInfo.verified).to.be.rejectedWith(/Signature is expired/);
  });

  it('Verification fails if signing key\'s self-sig were created after the time of signing, unless config allows it', async function() {
    const armoredReformattedKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEYWmlshYJKwYBBAHaRw8BAQdAAxpFNPiHxz9q4HBzWqveHdP/knjwlgv8
pEQCMHDpIZIAAP9WFlwHDuVlvNb7CyoikwmG01nmdMDe9wXQRWA5vasWKA+g
zSV0ZXN0QHJlZm9ybWF0LmNvbSA8dGVzdEByZWZvcm1hdC5jb20+wowEEBYK
AB0FAmFppjQECwkHCAMVCAoEFgACAQIZAQIbAwIeAQAhCRAOZNKOg+/XQxYh
BGqP/hIaYCSJsZ4TrQ5k0o6D79dD+c8BAIXdh2hrC+l49WPN/KZF+ZzvWCWa
W5n+ozbp/sOGXvODAP4oGEj0RUDDA33b6x7fhQysBZxdrrnHxP9AXEdOTQC3
CsddBGFppbISCisGAQQBl1UBBQEBB0Cjy8Z2K7rl6J6AK1lCfVozmyLE0Gbv
1cspce6oCF6oCwMBCAcAAP9OL5V80EaYm2ic19aM+NtTj4UNOqKqIt10AaH9
SlcdMBDgwngEGBYIAAkFAmFppjQCGwwAIQkQDmTSjoPv10MWIQRqj/4SGmAk
ibGeE60OZNKOg+/XQx/EAQCM0UYrObp60YbOCxu07Dm6XjCVylbOcsaxCnE7
2eMU4AD+OkgajZgbqSIdAR1ud76FW+W+3xlDi/SMFdU7D49SbQI=
=ASQu
-----END PGP PRIVATE KEY BLOCK-----`;
    const armoredMessage = `-----BEGIN PGP MESSAGE-----

xA0DAQoWDmTSjoPv10MByw91AGFpplFwbGFpbnRleHTCdQQBFgoABgUCYWml
sgAhCRAOZNKOg+/XQxYhBGqP/hIaYCSJsZ4TrQ5k0o6D79dDDWwBAKUnRWXj
P3HTW521iD/DngK54mYS3feQzhDokhkYjO3UAP0ZlsYShKaJvXh+JgvR5BPP
gjVcn04JVVlxqgVnMqeVBw==
=eyO7
-----END PGP MESSAGE-----`;
    // the key was reformatted and the message signature date preceeds the key self-signature creation date
    const key = await openpgp.readKey({ armoredKey: armoredReformattedKey });
    const { signatures: [sigInfoRejected] } = await openpgp.verify({
      verificationKeys: key,
      message: await openpgp.readMessage({ armoredMessage })
    });
    await expect(sigInfoRejected.verified).to.be.rejectedWith(/Signature creation time is in the future/);

    // since the key is valid at the current time, the message should be verifiable if the `config` allows it
    const { signatures: [sigInfoValid] } = await openpgp.verify({
      verificationKeys: key,
      message: await openpgp.readMessage({ armoredMessage }),
      config: { allowInsecureVerificationWithReformattedKeys: true }
    });
    expect(await sigInfoValid.verified).to.be.true;
  });

  it('Verification fails if signing key was already expired at the time of signing (one-pass signature, streamed)', async function() {
    const armoredMessage = `-----BEGIN PGP MESSAGE-----

xA0DAQgB4IT3RGwgLJcByxR1AGCc8BxIZWxsbyBXb3JsZCA6KcKzBAEBCAAG
BQJgnPAcACEJEOCE90RsICyXFiEE19Gx3sbKlGcANEXF4IT3RGwgLJdssAP+
KpyVi30z5iMckULAQ3Q34IB29Gxa1/99ABSld6iIVGRCfmZZlS5UGcxJJGoL
vZ1RAL4YQx/GLV1dBcKWFwzb5G2/ip4coDMCDGTAwnwjcPwjHpfMQ9gX59mG
AkLaG/AkATpuH+DMkYDmKbDLGgD+N4yuxXBJmBfC2IBe4J1S2Gg=
=zvNP
-----END PGP MESSAGE-----`;
    const key = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });
    const { privateKey: expiredKey } = await openpgp.reformatKey({
      privateKey: key,
      userIDs: key.users.map(user => user.userID),
      keyExpirationTime: 1,
      date: key.keyPacket.created,
      format: 'object'
    });
    const { data, signatures: [sigInfo] } = await openpgp.verify({
      verificationKeys: expiredKey,
      message: await openpgp.readMessage({ armoredMessage: stream.toStream(armoredMessage) }),
      config: { minRSABits: 1024 }
    });
    await stream.readToEnd(data);
    await expect(sigInfo.verified).to.be.rejectedWith(/Primary key is expired/);
  });

  it('Verification fails if signing key was already expired at the time of signing (standard signature)', async function() {
    const armoredMessage = `-----BEGIN PGP MESSAGE-----

wrMEAQEIAAYFAmCc8BwAIQkQ4IT3RGwgLJcWIQTX0bHexsqUZwA0RcXghPdE
bCAsl2ywA/4qnJWLfTPmIxyRQsBDdDfggHb0bFrX/30AFKV3qIhUZEJ+ZlmV
LlQZzEkkagu9nVEAvhhDH8YtXV0FwpYXDNvkbb+KnhygMwIMZMDCfCNw/CMe
l8xD2Bfn2YYCQtob8CQBOm4f4MyRgOYpsMsaAP43jK7FcEmYF8LYgF7gnVLY
aMsUdQBgnPAcSGVsbG8gV29ybGQgOik=
=s9xh
-----END PGP MESSAGE-----`;
    const key = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });
    const { privateKey: expiredKey } = await openpgp.reformatKey({
      privateKey: key,
      userIDs: key.users.map(user => user.userID),
      keyExpirationTime: 1,
      date: key.keyPacket.created,
      format: 'object'
    });
    const { data, signatures: [sigInfo] } = await openpgp.verify({
      verificationKeys: expiredKey,
      message: await openpgp.readMessage({ armoredMessage: stream.toStream(armoredMessage) }),
      config: { minRSABits: 1024 }
    });
    await stream.readToEnd(data);
    await expect(sigInfo.verified).to.be.rejectedWith(/Primary key is expired/);
  });

  it('Verification succeeds if an expired signing key was valid at the time of signing (with streaming)', async function() {
    const armoredMessage = `-----BEGIN PGP MESSAGE-----

xA0DAQgB4IT3RGwgLJcByxF1AGCdJvJoZWxsbyB3b3JsZMKzBAEBCAAGBQJS
YS9OACEJEOCE90RsICyXFiEE19Gx3sbKlGcANEXF4IT3RGwgLJcPBQP/csZd
9AhZQ3+dPkwlqlsXqYMlVEagYDavlUDI2CEJ2cn1rqHBuMlRkmYs7UqODku4
FhJ6WvghiEKx8vqghDuaUXmcKuXhYe+PomD4XBmpbURBXCdPnojTINUj7GPK
eSvSZutLuKKbidSYMLhWROPlwKc2GU2ws6PrLZAyCAel/lU=
=xDib
-----END PGP MESSAGE-----`;
    const key = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });
    const { privateKey: expiredKey } = await openpgp.reformatKey({
      privateKey: key,
      userIDs: key.users.map(user => user.userID),
      keyExpirationTime: 1,
      date: key.keyPacket.created,
      format: 'object'
    });
    const { data, signatures: [sigInfo] } = await openpgp.verify({
      verificationKeys: expiredKey,
      message: await openpgp.readMessage({ armoredMessage: stream.toStream(armoredMessage) }),
      config: { minRSABits: 1024 }
    });
    await stream.readToEnd(data);
    expect(await sigInfo.verified).to.be.true;
  });

  it('Verification succeeds if an expired signing key was valid at the time of signing (without streaming)', async function() {
    const armoredMessage = `-----BEGIN PGP MESSAGE-----

xA0DAQgB4IT3RGwgLJcByxF1AGCdJvJoZWxsbyB3b3JsZMKzBAEBCAAGBQJS
YS9OACEJEOCE90RsICyXFiEE19Gx3sbKlGcANEXF4IT3RGwgLJcPBQP/csZd
9AhZQ3+dPkwlqlsXqYMlVEagYDavlUDI2CEJ2cn1rqHBuMlRkmYs7UqODku4
FhJ6WvghiEKx8vqghDuaUXmcKuXhYe+PomD4XBmpbURBXCdPnojTINUj7GPK
eSvSZutLuKKbidSYMLhWROPlwKc2GU2ws6PrLZAyCAel/lU=
=xDib
-----END PGP MESSAGE-----`;
    const key = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });
    const { privateKey: expiredKey } = await openpgp.reformatKey({
      privateKey: key,
      userIDs: key.users.map(user => user.userID),
      keyExpirationTime: 1,
      date: key.keyPacket.created,
      format: 'object'
    });
    const { data, signatures: [sigInfo] } = await openpgp.verify({
      verificationKeys: expiredKey,
      message: await openpgp.readMessage({ armoredMessage }),
      config: { minRSABits: 1024 }
    });
    await stream.readToEnd(data);
    expect(await sigInfo.verified).to.be.true;
  });

  it('Verification fails if v6 signature has unexpected salt length', async function() {
    // signature with salt shorter than expected
    const armoredMessage = `-----BEGIN PGP MESSAGE-----

xDQGAQgbDpdDiCIrq6YZAf5vD3wFIucHRyMNlExatdj6sQcW2FA/vV5eZGCv
mBUS4Mqqki4ByxR1AGUddyNUaGlzIGlzIHNpZ25lZMKGBgEbCAAAACkFgmUd
dyMioQYi5wdHIw2UTFq12PqxBxbYUD+9Xl5kYK+YFRLgyqqSLgAAAADZ9w6X
Q4giK6umGQH+bw98BS96KSXxW39Ue6hNxbSoc5xOqYnTsD+75FYdR1U9fco/
HDpH7axPa2euIDpwT60NedSjcTx7C9Sots4mTvjMwQQ=
-----END PGP MESSAGE-----`;
    const key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xUsGZR13GRsAAAAgcCI5M7vPn+9uD1ii8nnT/schP5BjXXTyr+q7EmSlcaoA
/OkLygFTbUdwt6hMlfcNyUmS058WSIHxaVtG4uSfyjbCmQYfGwgAAAA6BYJl
HXcZAwsJBwMVCAoCFgACmwMCHgEioQYi5wdHIw2UTFq12PqxBxbYUD+9Xl5k
YK+YFRLgyqqSLgAAAABCZxAAxl8ycoAAY74DEPZDnfSYLP+dqdM8QZ3b/Mp4
fnzOcVI4RvaxAjp3GZVXxisSS36A2fUx2lpj38y1tIvnnlShfpuylTp73foT
DVf/bROnAM0AwosGEBsIAAAALAWCZR13GQIZASKhBiLnB0cjDZRMWrXY+rEH
FthQP71eXmRgr5gVEuDKqpIuAAAAAFEEEFrhrlN40SgxwpL3UaSWs6F5pD83
AOtaXLA/e9gFPNgiLnuid3AqUaFa6JlhWf4N/Md6SMQJ5cC7ATxTJ2a3xAMY
5UsE6+HN099QVLx95CMP
-----END PGP PRIVATE KEY BLOCK-----` });

    const { signatures } = await openpgp.verify({
      message: await openpgp.readMessage({ armoredMessage }),
      verificationKeys: key
    });
    expect(signatures).to.have.length(1);
    await expect(signatures[0].verified).to.be.rejectedWith(/Signature salt does not have the expected length/);
  });

  it('Reject cleartext message with arbitrary text added around hash headers (spoofed cleartext message)', async function() {
    await expect(openpgp.readCleartextMessage({ cleartextMessage: `-----BEGIN PGP SIGNED MESSAGE-----
This is not signed but you might think it is Hash: SHA512

This is signed
-----BEGIN PGP SIGNATURE-----

wnUEARYKACcFgmTsqxgJkEhlqJkkhIfRFiEEUA/OS4xZ3EwNC5l8SGWomSSE
h9EAALyPAQDDR0IYwq/5XMVSYPWojBamM4NhcP5arA656ALIq9cJYAEAlw0H
Fk7EflUZzngwY4lBzYAfnNBjEjc30xD/ddo+rwE=
=O7mt
-----END PGP SIGNATURE-----` })).to.be.rejectedWith(/Only "Hash" header allowed/);

    await expect(openpgp.readCleartextMessage({ cleartextMessage: `-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512\vThis is not signed but you might think it is 

This is signed
-----BEGIN PGP SIGNATURE-----

wnUEARYKACcFgmTsqxgJkEhlqJkkhIfRFiEEUA/OS4xZ3EwNC5l8SGWomSSE
h9EAALyPAQDDR0IYwq/5XMVSYPWojBamM4NhcP5arA656ALIq9cJYAEAlw0H
Fk7EflUZzngwY4lBzYAfnNBjEjc30xD/ddo+rwE=
=O7mt
-----END PGP SIGNATURE-----` })).to.be.rejectedWith(/Unknown hash algorithm in armor header/);
  });

  it('Supports non-human-readable notations', async function() {
    const { packets: [signature] } = await openpgp.readSignature({ armoredSignature: signature_with_non_human_readable_notations });
    // There are no human-readable notations so `notations` property does not
    // expose the `test@key.com` notation.
    expect(Object.keys(signature.notations).length).to.equal(0);
    expect(signature.notations['test@key.com']).to.equal(undefined);

    // The notation is readable through `rawNotations` property:
    expect(signature.rawNotations.length).to.equal(1);
    const notation = signature.rawNotations[0];
    expect(notation.name).to.equal('test@key.com');
    expect(notation.value).to.deep.equal(new Uint8Array([0x01, 0x02, 0x03]));
    expect(notation.humanReadable).to.equal(false);
  });

  it('Verify V4 signature. Hash: SHA1. PK: RSA. Signature Type: 0x00 (binary document)', async function() {
    const { rejectMessageHashAlgorithms, minRSABits } = openpgp.config;
    Object.assign(openpgp.config, {
      rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]),
      minRSABits: 1024
    });
    try {
      const signedArmor =
        ['-----BEGIN PGP MESSAGE-----',
          'Version: GnuPG v2.0.19 (GNU/Linux)',
          '',
          'owGbwMvMwMT4oOW7S46CznTGNeZJLCWpFSVBU3ZGF2fkF5Uo5KYWFyemp3LlAUUV',
          'cjLzUrneTp3zauvaN9O26L9ZuOFNy4LXyydwcXXMYWFgZGJgY2UCaWXg4hSAmblK',
          'nPmfsXYxd58Ka9eVrEnSpzilr520fXBrJsf2P/oTqzTj3hzyLG0o3TTzxFfrtOXf',
          'cw6U57n3/Z4X0pEZ68C5/o/6NpPICD7fuEOz3936raZ6wXGzueY8pfPnVjY0ajAc',
          'PtJzvvqj+ubYaT1sK9wWhd9lL3/V+9Zuua9QjOWC22buchsCroh8fLoZAA==',
          '=VH8F',
          '-----END PGP MESSAGE-----'].join('\n');

      const sMsg = await openpgp.readMessage({ armoredMessage: signedArmor });
      const pub_key = await openpgp.readKey({ armoredKey: pub_key_arm2 });
      const verified = await sMsg.verify([pub_key]);
      await stream.readToEnd(sMsg.getLiteralData());
      expect(verified).to.exist;
      expect(verified).to.have.length(1);
      expect(await verified[0].verified).to.be.true;
      expect((await verified[0].signature).packets.length).to.equal(1);
    } finally {
      Object.assign(openpgp.config, { rejectMessageHashAlgorithms, minRSABits });
    }
  });

  it('Verify signature of signed and encrypted message from GPG2 with openpgp.decrypt', async function() {
    const msg_armor =
      ['-----BEGIN PGP MESSAGE-----',
        'Version: GnuPG v2.0.19 (GNU/Linux)',
        '',
        'hIwD4IT3RGwgLJcBBADEBdm+GEW7IV1K/Bykg0nB0WYO08ai7/8/+Y/O9xu6RiU0',
        'q7/jWuKms7kSjw9gxMCjf2dGnAuT4Cg505Kj5WfeBuHh618ovO8qo4h0qHyp1/y3',
        'o1P0IXPAb+LGJOeO7DyM9Xp2AOBiIKOVWzFTg+MBZOc+XZEVx3FioHfm9SSDudLA',
        'WAEkDakCG6MRFj/7SmOiV8mQKH+YPMKT69eDZW7hjINabrpM2pdRU7c9lC7CMUBx',
        'Vj7wZsQBMASSC8f2rhpGA2iKvYMsmW3g9R1xkvj1MXWftSPUS4jeNTAgEwvvF6Af',
        'cP+OYSXKlTbwfEr73ES2O3/IFE9sHRjPqWaxWuv4DDQ5YfIxE54C1aE8Aq5/QaIH',
        'v38TUSia0yEMCc/tJd58DikkT07AF162tcx9Ro0ZjhudyuvUyXIfPfxA+XWR2pdz',
        'ifxyV4zia9RvaCUY8vXGM+gQJ3NNXx2LkZA3kWUEyxFVL1Vl/XUQY0M6U+uccSk4',
        'eMXm6eyEWDcj0lBRckqKoKo1w/uan11jPuHsnRz6jO9DsuKEz79UDgI=',
        '=cFi7',
        '-----END PGP MESSAGE-----'].join('\n');

    const plaintext = 'short message\nnext line\n한국어/조선말';
    const message = await openpgp.readMessage({ armoredMessage: msg_armor });
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const privKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });

    return openpgp.decrypt({
      decryptionKeys: privKey, verificationKeys: pubKey , message, config: { minRSABits: 1024 }
    }).then(async ({ signatures, data }) => {
      expect(data).to.exist;
      expect(data).to.equal(plaintext);
      expect(signatures).to.have.length(1);
      expect(await signatures[0].verified).to.be.true;
      expect((await signatures[0].signature).packets.length).to.equal(1);
    });
  });


  it('Verify signed message with a v6 one pass signature', async function() {
    // test vector from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#appendix-A.7
    const message = await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE-----

xEYGAQobIHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usyxhsTwYJppfk
1S36bHIrDB8eJ8GKVnCPZSXsJ7rZrMkBy0p1AAAAAABXaGF0IHdlIG5lZWQgZnJv
bSB0aGUgZ3JvY2VyeSBzdG9yZToKCi0gdG9mdQotIHZlZ2V0YWJsZXMKLSBub29k
bGVzCsKYBgEbCgAAACkFgmOYo2MiIQbLGGxPBgmml+TVLfpscisMHx4nwYpWcI9l
JewnutmsyQAAAABpNiB2SV9QIYiQ9/Xi7jwYIlFPcFAPVR2G5ckh5ATjSlP7rCfQ
b7gKqPxbyxbhljGygHQPnqau1eBzrQD5QVplPEDnemrnfmkrpx0GmhCfokxYz9jj
FtCgazStmsuOXF9SFQE=
-----END PGP MESSAGE-----` });
    const key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----

xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf
GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy
KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw
gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE
QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn
+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh
BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8
j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805
I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==
-----END PGP PUBLIC KEY BLOCK-----` });

    const signingKeyIDs = message.getSigningKeyIDs();
    expect(key.getKeys(signingKeyIDs[0])).to.not.be.empty;

    const { data, signatures } = await openpgp.verify({ message, verificationKeys: key });
    expect(signatures).to.have.length(1);
    expect(await signatures[0].verified).to.be.true;
    expect((await signatures[0].signature).packets.length).to.equal(1);
    expect(data.includes('What we need from the grocery store')).to.be.true;
  });

  it('Verify signed message with two one pass signatures (v3 and v6)', async function() {
    // test vector from gopenpgp
    const message = await openpgp.readMessage({ armoredMessage: `-----BEGIN PGP MESSAGE-----

xEYGAAobIBEtnAy3EeX8aDFd2bf/A1Xfi7C/D3mLIkGEwVmD0fecyxhsTwYJppfk
1S36bHIrDB8eJ8GKVnCPZSXsJ7rZrMkAxA0DAAoW8jFVDE9H444ByxRiAAAAAABI
ZWxsbyBXb3JsZCA6KcJ1BAAWCgAnBQJk52D2CRDyMVUMT0fjjhYhBOuFu1+jOnXh
XpROY/IxVQxPR+OOAABOSwEA2nLYxa9ELDxwuPskKCUVs8noyT4fVEScWlWZAKqP
ykIBAJEhqqNHFP4Gok1Ss9mvf6fE25tJkdJKD+7PIH0mCJgAwpgGABsKAAAAKQUC
ZOdg9iKhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAA2MIBEt
nAy3EeX8aDFd2bf/A1Xfi7C/D3mLIkGEwVmD0fecr0oTPTcMNupiH7SCY4THe3ue
7PlRvF2WoeWKkNZT1uwcb+ilW5h9eBpp0I4Xj98C0hMA5+rHsTsME2EZM8HADA==
-----END PGP MESSAGE-----` });
    const v6Key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----

xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf
GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy
KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw
gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE
QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn
+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh
BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8
j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805
I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==
-----END PGP PUBLIC KEY BLOCK-----` });
    const v4Key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: Alice's OpenPGP certificate

mDMEXEcE6RYJKwYBBAHaRw8BAQdArjWwk3FAqyiFbFBKT4TzXcVBqPTB3gmzlC/U
b7O1u120JkFsaWNlIExvdmVsYWNlIDxhbGljZUBvcGVucGdwLmV4YW1wbGU+iJAE
ExYIADgCGwMFCwkIBwIGFQoJCAsCBBYCAwECHgECF4AWIQTrhbtfozp14V6UTmPy
MVUMT0fjjgUCXaWfOgAKCRDyMVUMT0fjjukrAPoDnHBSogOmsHOsd9qGsiZpgRnO
dypvbm+QtXZqth9rvwD9HcDC0tC+PHAsO7OTh1S1TC9RiJsvawAfCPaQZoed8gK4
OARcRwTpEgorBgEEAZdVAQUBAQdAQv8GIa2rSTzgqbXCpDDYMiKRVitCsy203x3s
E9+eviIDAQgHiHgEGBYIACAWIQTrhbtfozp14V6UTmPyMVUMT0fjjgUCXEcE6QIb
DAAKCRDyMVUMT0fjjlnQAQDFHUs6TIcxrNTtEZFjUFm1M0PJ1Dng/cDW4xN80fsn
0QEA22Kr7VkCjeAEC08VSTeV+QFsmz55/lntWkwYWhmvOgE=
=iIGO
-----END PGP PUBLIC KEY BLOCK-----` });

    const { data, signatures } = await openpgp.verify({ message, verificationKeys: [v4Key, v6Key] });
    expect(signatures).to.have.length(2);
    expect(await signatures[0].verified).to.be.true;
    expect((await signatures[0].signature).packets.length).to.equal(1);
    expect(await signatures[1].verified).to.be.true;
    expect((await signatures[1].signature).packets.length).to.equal(1);
    expect(data).to.equal('Hello World :)');
  });

  it('Verify signed message with two one pass signatures (both v3)', async function() {
    const msg_armor =
      ['-----BEGIN PGP MESSAGE-----',
        'Version: GnuPG v2.0.19 (GNU/Linux)',
        '',
        'owGbwMvMwMF4+5Pyi4Jg3y8ME8DcBy3fXXIUdKYzrjFNYilJrSgJmsXDXJyRX1Si',
        'kJtaXJyYnsqVBxRVyMnMS+V6O3XOq61r30zbov9m4YY3LQteL5/QMYeFgZGDgY2V',
        'CaSRgYtTAGZiYxYLwySbQk07ptZel6gmjrKyBWsyWdkOG3oscLBdIpXXfDdb6fNv',
        '8ULN5L1ed+xNo79P2dBotWud6vn7e9dtLJ7o12PunnvEz8gyyvP4/As/los0xsnZ',
        'H+8ublrhvGtLxJUZuUKZO6QdHq2Nepuw8OrfiMXPBDQXXpV2q11Ze+rD3lndgv/C',
        'bJQNOhll0J0H839jFvt/16m20h/ZmDoWqJywapnypjdIjcXr+7rJFess40yenV7Q',
        '2LSu/EX6Aq29x+dv+GPUMfuhTNE3viWWUR4PD6T7XfmdViUwmSf8fkRNUn/t3a2n',
        'cq46Xr36seCor/OLp0atSZwHrjx2SU5zPLheZn+zw/0d1/YZnD7AEeP9s/Cuycyv',
        'CZ5HZNKufvB8fsh+dfdSXW0GfqkPfxk36Vw8ufpjaoZDyt2nxxg/6D4KS3UvZzv3',
        'axdLZ9yd0OJNZv4P501If24W4vTGz6nI7Ser8Yd2PiOvE5MWMT0wLZQ+zPX1sv0/',
        's8PvkyWmVM0O0fB/ZSHovHNNPffDg/rWhzOmXQ9/7vTn477F+aWm5sYzJ75/BQA=',
        '=+L0S',
        '-----END PGP MESSAGE-----'].join('\n');
    const plaintext = 'short message\nnext line\n한국어/조선말';
    const sMsg = await openpgp.readMessage({ armoredMessage: msg_armor });
    const pubKey2 = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const pubKey3 = await openpgp.readKey({ armoredKey: pub_key_arm3 });

    const keyIDs = sMsg.getSigningKeyIDs();
    expect(pubKey2.getKeys(keyIDs[1])).to.not.be.empty;
    expect(pubKey3.getKeys(keyIDs[0])).to.not.be.empty;

    const { data, signatures } = await openpgp.verify({ message: sMsg, verificationKeys: [pubKey2, pubKey3], config: { minRSABits: 1024 } });
    expect(data).to.equal(plaintext);
    expect(signatures).to.exist;
    expect(signatures).to.have.length(2);
    expect(await signatures[0].verified).to.be.true;
    expect(await signatures[1].verified).to.be.true;
    expect((await signatures[0].signature).packets.length).to.equal(1);
    expect((await signatures[1].signature).packets.length).to.equal(1);
  });

  it('Verify fails with signed message with critical notations', async function() {
    const message = await openpgp.readMessage({ armoredMessage: signature_with_critical_notation });
    const key = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const { signatures: [sig] } = await openpgp.verify({ message, verificationKeys: key, config: { minRSABits: 1024 } });
    await expect(sig.verified).to.be.rejectedWith(/Unknown critical notation: test@example.com/);
  });

  it('Verify succeeds with known signed message with critical notations', async function() {
    const message = await openpgp.readMessage({ armoredMessage: signature_with_critical_notation });
    const key = await openpgp.readKey({ armoredKey: pub_key_arm2 });

    const config = { knownNotations: ['test@example.com'], minRSABits: 1024 };
    const { signatures: [sig] } = await openpgp.verify({ message, verificationKeys: key, config });
    expect(await sig.verified).to.be.true;
  });

  it('Can create notations', async function() {
    const privKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });

    const config = { minRSABits: 1024 };
    const message_with_notation = await openpgp.encrypt({
      message: await openpgp.createMessage({ text: 'test' }),
      encryptionKeys: privKey,
      signingKeys: privKey,
      signatureNotations: [
        {
          name: 'test@example.com',
          value: new TextEncoder().encode('test'),
          humanReadable: true,
          critical: true
        },
        {
          name: 'séparation-de-domaine@proton.ch',
          value: new Uint8Array([0, 1, 2, 3]),
          humanReadable: false,
          critical: false
        }
      ],
      config
    });
    await expect(openpgp.decrypt({
      message: await openpgp.readMessage({ armoredMessage: message_with_notation }),
      decryptionKeys: privKey,
      verificationKeys: privKey,
      expectSigned: true,
      config
    })).to.be.rejectedWith('Unknown critical notation: test@example.com');
    const { signatures: [sig] } = await openpgp.decrypt({
      message: await openpgp.readMessage({ armoredMessage: message_with_notation }),
      decryptionKeys: privKey,
      verificationKeys: privKey,
      config: {
        knownNotations: ['test@example.com'],
        ...config
      }
    });
    expect(await sig.verified).to.be.true;
    const { packets: [{ rawNotations: notations }] } = await sig.signature;
    expect(notations).to.have.length(3);
    expect(notations[0].name).to.equal('test@example.com');
    expect(notations[0].value).to.deep.equal(new Uint8Array([116, 101, 115, 116]));
    expect(notations[0].humanReadable).to.be.true;
    expect(notations[0].critical).to.be.true;
    expect(notations[1].name).to.equal('séparation-de-domaine@proton.ch');
    expect(notations[1].value).to.deep.equal(new Uint8Array([0, 1, 2, 3]));
    expect(notations[1].humanReadable).to.be.false;
    expect(notations[1].critical).to.be.false;
    expect(notations[2].name).to.equal('salt@notations.openpgpjs.org');
    expect(notations[2].humanReadable).to.be.false;
    expect(notations[2].critical).to.be.false;
  });

  it('v4 signatures are randomized via salt notation (`config.nonDeterministicSignaturesViaNotation`)', async function() {
    const v4SigningKey = await openpgp.readKey({
      armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEX8+jfBYJKwYBBAHaRw8BAQdA9GbdDjprR0sWf0R5a5IpulUauc0FsmzJ
mOYCfoowt8EAAP9UwaqC0LWWQ5RlX7mps3728vFa/If1KBVwAjk7Uqhi2BKL
zQ90ZXN0MiA8YkBhLmNvbT7CjAQQFgoAHQUCX8+jfAQLCQcIAxUICgQWAgEA
AhkBAhsDAh4BACEJEG464aV2od77FiEEIcg441MtKnyJnPDRbjrhpXah3vuR
gQD+Il6Gw2oIok4/ANyDDLBYZtKqRrMv4NcfF9DHYuAFcP4BAPhFOffyP3qU
AEZb7QPrWdLfhn8/FeSFZxJvnmupQ9sDx10EX8+jfBIKKwYBBAGXVQEFAQEH
QOSzo9cX1U2esGFClprOt0QWXNJ97228R5tKFxo6/0NoAwEIBwAA/0n4sq2i
N6/jE+6rVO4o/7LW0xahxpV1tTA6qv1Op9TwFIDCeAQYFggACQUCX8+jfAIb
DAAhCRBuOuGldqHe+xYhBCHIOONTLSp8iZzw0W464aV2od773XcA/jlmX8/c
1/zIotEkyMZB4mI+GAg3FQ6bIACFBH1sz0MzAP9Snri0P4FRZ8D5THRCJoUm
GBgpBmrf6IVv484jBswGDA==
=8rBO
-----END PGP PRIVATE KEY BLOCK-----`
    });

    const date = new Date('Tue, 25 Dec 2023 00:00:00 GMT');
    const text = 'test';
    const armoredRandomSignature1 = await openpgp.sign({
      message: await openpgp.createMessage({ text }),
      signingKeys: v4SigningKey,
      date,
      detached: true
    });
    const armoredRandomSignature2 = await openpgp.sign({
      message: await openpgp.createMessage({ text }),
      signingKeys: v4SigningKey,
      date,
      detached: true
    });
    const randomSignature1 = await openpgp.readSignature({ armoredSignature: armoredRandomSignature1 });
    const randomSignature2 = await openpgp.readSignature({ armoredSignature: armoredRandomSignature2 });
    expect(randomSignature1.packets[0].signedHashValue).to.not.deep.equal(randomSignature2.packets[0].signedHashValue);

    // ensure the signatures are verifiable, as a sanity check
    const verification1 = await openpgp.verify({ message: await openpgp.createMessage({ text }), signature: randomSignature1, verificationKeys: v4SigningKey, expectSigned: true });
    expect(verification1.data).to.equal(text);

    const armoredDeterministicSignature1 = await openpgp.sign({
      message: await openpgp.createMessage({ text }),
      signingKeys: v4SigningKey,
      date,
      detached: true,
      config: { nonDeterministicSignaturesViaNotation: false }
    });
    const armoredDeterministicSignature2 = await openpgp.sign({
      message: await openpgp.createMessage({ text }),
      signingKeys: v4SigningKey,
      date,
      detached: true,
      config: { nonDeterministicSignaturesViaNotation: false }
    });
    const deterministicSignature1 = await openpgp.readSignature({ armoredSignature: armoredDeterministicSignature1 });
    const deterministicSignature2 = await openpgp.readSignature({ armoredSignature: armoredDeterministicSignature2 });
    expect(deterministicSignature1.packets[0].signedHashValue).to.deep.equal(deterministicSignature2.packets[0].signedHashValue);
    const verification2 = await openpgp.verify({ message: await openpgp.createMessage({ text }), signature: deterministicSignature1, verificationKeys: v4SigningKey, expectSigned: true });
    expect(verification2.data).to.equal(text);
  });

  it('Verify v6 cleartext signed message with openpgp.verify', async function() {
    // test vector from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-08.html#name-sample-v6-certificate-trans
    const cleartextMessage = `-----BEGIN PGP SIGNED MESSAGE-----

What we need from the grocery store:

- - tofu
- - vegetables
- - noodles

-----BEGIN PGP SIGNATURE-----

wpgGARsKAAAAKQWCY5ijYyIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJAAAAAGk2IHZJX1AhiJD39eLuPBgiUU9wUA9VHYblySHkBONKU/usJ9BvuAqo
/FvLFuGWMbKAdA+epq7V4HOtAPlBWmU8QOd6aud+aSunHQaaEJ+iTFjP2OMW0KBr
NK2ay45cX1IVAQ==
-----END PGP SIGNATURE-----`;

    const publicKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----

xioGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laPCsQYf
GwoAAABCBYJjh3/jAwsJBwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxy
KwwfHifBilZwj2Ul7Ce62azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lw
gyU2kCcUmKfvBXbAf6rhRYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaE
QsiPlR4zxP/TP7mhfVEe7XWPxtnMUMtf15OyA51YBM4qBmOHf+MZAAAAIIaTJINn
+eUBXbki+PSAld2nhJh/LVmFsS+60WyvXkQ1wpsGGBsKAAAALAWCY4d/4wKbDCIh
BssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce62azJAAAAAAQBIKbpGG2dWTX8
j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDEM0g12vYxoWM8Y81W+bHBw805
I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUrk0mXubZvyl4GBg==
-----END PGP PUBLIC KEY BLOCK-----` });

    const plaintext = 'What we need from the grocery store:\n\n- tofu\n- vegetables\n- noodles\n';
    const message = await openpgp.readCleartextMessage({ cleartextMessage });

    const { signatures, data } = await openpgp.verify({ message, verificationKeys: publicKey });
    expect(data).to.equal(plaintext);
    expect(signatures).to.have.length(1);
    expect(await signatures[0].verified).to.be.true;
    expect((await signatures[0].signature).packets.length).to.equal(1);
  });

  it('Verify cleartext signed message with two signatures with openpgp.verify', async function() {
    const cleartextMessage =
      ['-----BEGIN PGP SIGNED MESSAGE-----',
        'Hash: SHA256',
        '',
        'short message',
        'next line',
        '한국어/조선말',
        '-----BEGIN PGP SIGNATURE-----',
        'Version: GnuPG v2.0.19 (GNU/Linux)',
        '',
        'iJwEAQEIAAYFAlKcju8ACgkQ4IT3RGwgLJci6gP/dCmIraUa6AGpJxzGfK+jYpjl',
        'G0KunFyGmyPxeJVnPi2bBp3EPIbiayQ71CcDe9DKpF046tora07AA9eo+/YbvJ9P',
        'PWeScw3oj/ejsmKQoDBGzyDMFUphevnhgc5lENjovJqmiu6FKjNmADTxcZ/qFTOq',
        '44EWTgdW3IqXFkNpKjeJARwEAQEIAAYFAlKcju8ACgkQ2/Ij6HBTTfQi6gf9HxhE',
        'ycLDhQ8iyC090TaYwsDytScU2vOMiI5rJCy2tfDV0pfn+UekYGMnKxZTpwtmno1j',
        'mVOlieENszz5IcehS5TYwk4lmRFjoba+Z8qwPEYhYxP29GMbmRIsH811sQHFTigo',
        'LI2t4pSSSUpAiXd9y6KtvkWcGGn8IfkNHCEHPh1ov28QvH0+ByIiKYK5N6ZB8hEo',
        '0uMYhKQPVJdPCvMkAxQCRPw84EvmxuJ0HMCeSB9tHQXpz5un2m8D9yiGpBQPnqlW',
        'vCCq7fgaUz8ksxvQ9bSwv0iIIbbBdTP7Z8y2c1Oof6NDl7irH+QCeNT7IIGs8Smn',
        'BEzv/FqkQAhjy3Krxg==',
        '=3Pkl',
        '-----END PGP SIGNATURE-----'].join('\n');

    const plaintext = 'short message\nnext line\n한국어/조선말';
    const message = await openpgp.readCleartextMessage({ cleartextMessage });
    const pubKey2 = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const pubKey3 = await openpgp.readKey({ armoredKey: pub_key_arm3 });

    const keyIDs = message.getSigningKeyIDs();

    expect(pubKey2.getKeys(keyIDs[0])).to.not.be.empty;
    expect(pubKey3.getKeys(keyIDs[1])).to.not.be.empty;

    return openpgp.verify({ verificationKeys:[pubKey2, pubKey3], message, config: { minRSABits: 1024 } }).then(async function({ signatures, data }) {
      expect(data).to.equal(plaintext);
      expect(signatures).to.have.length(2);
      expect(await signatures[0].verified).to.be.true;
      expect(await signatures[1].verified).to.be.true;
      expect((await signatures[0].signature).packets.length).to.equal(1);
      expect((await signatures[1].signature).packets.length).to.equal(1);
    });
  });

  it('Verify latin-1 signed message', async function() {
    const latin1Binary = util.hexToUint8Array('48e46c6cf62057e86c74');
    const message = await openpgp.createMessage({ binary: latin1Binary });

    await message.appendSignature(`-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEET5+J9VBawdGiYGMc2xGHud1faTsFAl5lE/AACgkQ2xGHud1f
aTtIuw//YWrVaXLyP8sGBc0uUSLxQbmfQQYV8Oq8Vsg+jV4orc73wmEy8+Nj5m2g
fFEPaWy07dfDBtv874XCsZmCM+ZhwkGaT9lwcCxkxNZeywTE5JRS1/6Ky3G4gDZ/
QozTXr/ZNPXF6bBENqhfqeO2xkD577bjiPu5wLcu3/RR39YnWp5zQu9ynJbpwobz
HHQW5TgSUgi/9tInQ+cc7vMkHzfe2Zg45HkyaStBW1x7Fm9FLv8GNw1R2jVbUlsX
SEL7yPrsZAmgyu1ifMrTTY4vunuUNUFiGZN9UC75b7s4tjKkvJ0sfdQusC0vmcE0
Wq7dAoeX72B94TITuXldDGIfL01smycm+mEvf/kXxQg81wj4la72IzhdWeC6r4PF
558QDEtZy984iL8RJKpbjwgsxyvfM9zf9qtmuNdscbZaZsx9LRe21uaKhX8yJsAU
jCgyjM5e7CMdvBUyxrpGzS+mP/rh77r2TXkg6u2+8Nj4osxwkfxvDIGpJfaIWl2a
sNQ4Bsgm6rcNYaxUVbOsBFJddBb503KpTX5aK8TuC6AINg6bV6rmfj2Jc/QX4eHb
Xgm6EyJo74z5R7YvA3XdUZf1sfwfoMLbbUqpeHEZOcWy4vrS79Omx1MyPxKHPiXU
PAAeuQTUrcJdZeJ86eQ9cCUB216HCwSKOWTQRzL+hBWKXij4WD4=
=ZEFm
-----END PGP SIGNATURE-----`);

    const pubKey = await openpgp.readKey({ armoredKey: pub_latin1_msg });

    return message.verify([pubKey]).then(async verifiedSig => {
      expect(await stream.readToEnd(message.getLiteralData())).to.equal(latin1Binary);
      expect(verifiedSig).to.exist;
      expect(verifiedSig).to.have.length(1);
      expect(await verifiedSig[0].verified).to.be.true;
      expect((await verifiedSig[0].signature).packets.length).to.equal(1);
    });
  });


  it('Verify cleartext signed message with trailing spaces from GPG', async function() {
    const cleartextMessage =
      `-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

space: 
space and tab: \t
no trailing space

tab:\t
tab and space:\t 
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iJwEAQECAAYFAlrZzCQACgkQ4IT3RGwgLJeWggP+Pb33ubbELIzg9/imM+zlR063
g0FbG4B+RGZNFSbaDArUgh9fdVqBy8M9vvbbDMBalSiQxY09Lrasfb+tsomrygbN
NisuPRa5phPhn1bB4hZDb2ed/iK41CNyU7QHuv4AAvLC0mMamRnEg0FW2M2jZLGh
zmuVOdNuWQqxT9Sqa84=
=bqAR
-----END PGP SIGNATURE-----`;

    const plaintext = 'space: \nspace and tab: \t\nno trailing space\n  \ntab:\t\ntab and space:\t ';
    const message = await openpgp.readCleartextMessage({ cleartextMessage });
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });

    const keyIDs = message.getSigningKeyIDs();

    expect(pubKey.getKeys(keyIDs[0])).to.not.be.empty;

    const { signatures, data } = await openpgp.verify({
      verificationKeys:[pubKey],
      message,
      config: { minRSABits: 1024, rejectMessageHashAlgorithms: new Set() }
    });
    expect(data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
    expect(signatures).to.have.length(1);
    expect(await signatures[0].verified).to.be.true;
    expect((await signatures[0].signature).packets.length).to.equal(1);
  });

  it('Verify cleartext signed message with trailing spaces incorrectly normalised (from OpenPGP.js v3.0.9-v5.3.1)', async function() {
    // We used to not strip trailing whitespace with \r\n line endings when signing cleartext messages
    const armoredSignature = `-----BEGIN PGP SIGNATURE-----
Version: OpenPGP.js v5.3.1

wrMEAQEIAAYFAmLjqsQAIQkQ4IT3RGwgLJcWIQTX0bHexsqUZwA0RcXghPdE
bCAsl2TvBADLOHYXevDSc3LtLRYR1HteijL/MssCCoZIfuGihd5AzJpD2h2j
L8UuxlfERJn15RlFsDzlkMNMefJp5ltC8kKcf+HTuBUi+xf2t2nvlf8CrdjY
vcEqswjkODDxxZ842h0sC0ZtbzWuMXIvODEdzZxBjhlmZmv9VKQ5uyb0oD/5
WQ==
=o2gq
-----END PGP SIGNATURE-----`;
    const cleartextMessage = `
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

this text    
used to be incorrectly normalised
${armoredSignature}
`;

    const signedText = 'this text    \r\nused to be incorrectly normalised';
    const message = await openpgp.readCleartextMessage({ cleartextMessage });
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });

    // Direct verification won't work since the signed data was not stripped of the trailing whitespaces,
    // as required for cleartext messages. Verification would always fail also in the affected OpenPGP.js versions.
    await expect(openpgp.verify({
      verificationKeys:[pubKey],
      message,
      config: { minRSABits: 1024 },
      expectSigned: true
    })).to.be.rejectedWith(/Signed digest did not match/);

    // The signature should be verifiable over non-normalised text
    const { signatures, data } = await openpgp.verify({
      verificationKeys:[pubKey],
      message: await openpgp.createMessage({ text: signedText }),
      signature: await openpgp.readSignature({ armoredSignature }),
      config: { minRSABits: 1024 },
      expectSigned: true
    });
    expect(data).to.equal(signedText);
    expect(signatures).to.have.length(1);
    expect(await signatures[0].verified).to.be.true;
  });

  it('Sign and verify cleartext signed message with trailing spaces correctly normalised', async function() {
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const privKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });
    const config = { minRSABits: 1024 };

    const message = await openpgp.createCleartextMessage({
      text: 'this text    \r\nused to be incorrectly normalised'
    });
    const expectedText = 'this text\nused to be incorrectly normalised';
    expect(message.getText()).to.equal(expectedText);
    const cleartextMessage = await openpgp.sign({ message, signingKeys: privKey, config, format: 'armored' });
    const { signatures, data } = await openpgp.verify({
      message: await openpgp.readCleartextMessage({ cleartextMessage }),
      verificationKeys:[pubKey],
      config
    });
    expect(data).to.equal(expectedText);
    expect(signatures).to.have.length(1);
    expect(await signatures[0].verified).to.be.true;
  });

  it('Does not emit headers for v6 cleartext message', async function() {
    const v6PrivateKey = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB
exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ
BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh
RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe
7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/
LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG
GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6
2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE
M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr
k0mXubZvyl4GBg==
-----END PGP PRIVATE KEY BLOCK-----` });
    const v4PrivateKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });

    const message = await openpgp.createCleartextMessage({ text: 'check header message' });

    const cleartextMessageV6 = await openpgp.sign({ message, signingKeys: v6PrivateKey });
    const cleartextMessageV4 = await openpgp.sign({
      message,
      signingKeys: [v4PrivateKey, v6PrivateKey],
      config: { minRSABits: 1024 }
    });
    expect(cleartextMessageV6).to.not.contain('Hash:');
    expect(cleartextMessageV4).to.contain('Hash:');
  });

  function tests() {
    it('Verify signed message with trailing spaces from GPG', async function() {
      const armoredMessage =
        `-----BEGIN PGP MESSAGE-----
Version: GnuPG v1

owGbwMvMyMT4oOW7S46CznTG01El3MUFicmpxbolqcUlUTev14K5Vgq8XGCGQmJe
ikJJYpKVAicvV16+QklRYmZOZl66AliWl0sBqBAkzQmmwKohBnAqdMxhYWRkYmBj
ZQIZy8DFKQCztusM8z+Vt/svG80IS/etn90utv/T16jquk69zPvp6t9F16ryrwpb
kfVlS5Xl38KnVYxWvIor0nao6WUczA4vvZX9TXPWnnW3tt1vbZoiqWUjYjjjhuKG
4DtmMTuL3TW6/zNzVfWp/Q11+71O8RGnXMsBvWM6mSqX75uLiPo6HRaUDHnvrfCP
yYDnCgA=
=15ki
-----END PGP MESSAGE-----`;

      const plaintext = 'space: \nspace and tab: \t\nno trailing space\n  \ntab:\t\ntab and space:\t ';
      const message = await openpgp.readMessage({ armoredMessage });
      const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });

      const keyIDs = message.getSigningKeyIDs();
      expect(pubKey.getKeys(keyIDs[0])).to.not.be.empty;

      return openpgp.verify({ verificationKeys: [pubKey], message, config: { minRSABits: 1024 } }).then(async ({ data, signatures }) => {
        expect(data).to.equal(plaintext);
        expect(signatures).to.have.length(1);
        if (openpgp.config.rejectMessageHashAlgorithms.has(openpgp.enums.hash.sha1)) {
          await expect(signatures[0].verified).to.be.rejected;
        } else {
          expect(await signatures[0].verified).to.be.true;
        }
        expect((await signatures[0].signature).packets.length).to.equal(1);
      });
    });

    it('Streaming verify signed message with trailing spaces from GPG', async function() {
      const armoredMessage =
        `-----BEGIN PGP MESSAGE-----
Version: GnuPG v1

owGbwMvMyMT4oOW7S46CznTG01El3MUFicmpxbolqcUlUTev14K5Vgq8XGCGQmJe
ikJJYpKVAicvV16+QklRYmZOZl66AliWl0sBqBAkzQmmwKohBnAqdMxhYWRkYmBj
ZQIZy8DFKQCztusM8z+Vt/svG80IS/etn90utv/T16jquk69zPvp6t9F16ryrwpb
kfVlS5Xl38KnVYxWvIor0nao6WUczA4vvZX9TXPWnnW3tt1vbZoiqWUjYjjjhuKG
4DtmMTuL3TW6/zNzVfWp/Q11+71O8RGnXMsBvWM6mSqX75uLiPo6HRaUDHnvrfCP
yYDnCgA=
=15ki
-----END PGP MESSAGE-----`.split('');

      const plaintext = 'space: \nspace and tab: \t\nno trailing space\n  \ntab:\t\ntab and space:\t ';
      const message = await openpgp.readMessage({
        armoredMessage: new ReadableStream({
          async pull(controller) {
            await new Promise(setTimeout);
            controller.enqueue(armoredMessage.shift());
            if (!armoredMessage.length) controller.close();
          }
        })
      });
      const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });

      const keyIDs = message.getSigningKeyIDs();
      expect(pubKey.getKeys(keyIDs[0])).to.not.be.empty;

      return openpgp.verify({ verificationKeys: [pubKey], message, config: { minRSABits: 1024 } }).then(async function(cleartextSig) {
        expect(cleartextSig).to.exist;
        expect(await stream.readToEnd(cleartextSig.data)).to.equal(plaintext);
        expect(cleartextSig.signatures).to.have.length(1);
        if (!openpgp.config.rejectMessageHashAlgorithms.has(openpgp.enums.hash.sha1)) {
          expect(await cleartextSig.signatures[0].verified).to.be.true;
        } else {
          await expect(cleartextSig.signatures[0].verified).to.be.rejectedWith('Insecure message hash algorithm: SHA1');
        }
        expect((await cleartextSig.signatures[0].signature).packets.length).to.equal(1);
      });
    });

    it('Verify signed message with missing signature packet', async function() {
      const armoredMessage =
        `-----BEGIN PGP MESSAGE-----
Version: OpenPGP.js v3.1.3
Comment: https://openpgpjs.org

yFgBO8LLzMjE+KDlu0uOgs50xtNRJdzFBYnJqcW6JanFJVE3r9eCuVYKvFxg
hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA

=D6TZ
-----END PGP MESSAGE-----`;

      const plaintext = 'space: \nspace and tab: \t\nno trailing space\n  \ntab:\t\ntab and space:\t ';
      const message = await openpgp.readMessage({ armoredMessage, config: { enforceGrammar: false } });
      const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });

      const keyIDs = message.getSigningKeyIDs();
      expect(pubKey.getKeys(keyIDs[0])).to.not.be.empty;

      return openpgp.verify({ verificationKeys: [pubKey], message, config: { minRSABits: 1024 } }).then(({ data, signatures }) => {
        expect(data).to.equal(plaintext);
        expect(signatures).to.have.length(0);
      });
    });

    it('Streaming verify signed message with missing signature packet', async function() {
      const plaintext = 'space: \nspace and tab: \t\nno trailing space\n  \ntab:\t\ntab and space:\t ';
      const getStreamedMessage = config => {
        const armoredMessage = `-----BEGIN PGP MESSAGE-----
Version: OpenPGP.js v3.1.3
Comment: https://openpgpjs.org

yFgBO8LLzMjE+KDlu0uOgs50xtNRJdzFBYnJqcW6JanFJVE3r9eCuVYKvFxg
hkJiXopCSWKSlQInL1devkJJUWJmTmZeugJYlpdLAagQJM0JpsCqIQZwKgAA

=D6TZ
-----END PGP MESSAGE-----`.split('');
        return openpgp.readMessage({
          armoredMessage: new ReadableStream({
            async pull(controller) {
              await new Promise(setTimeout);
              controller.enqueue(armoredMessage.shift());
              if (!armoredMessage.length) controller.close();
            }
          }),
          config
        });
      };
      const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });

      const message = await getStreamedMessage();
      const messageWithoutGrammar = await getStreamedMessage({ enforceGrammar: false });

      const keyIDs = message.getSigningKeyIDs();
      expect(pubKey.getKeys(keyIDs[0])).to.not.be.empty;

      await openpgp.verify({ verificationKeys: [pubKey], message, config: { minRSABits: 1024 } }).then(async ({ data, signatures }) => {
        await expect(stream.readToEnd(data)).to.be.rejectedWith('Missing trailing signature packets');
        expect(signatures).to.have.length(1);
        await expect(signatures[0].verified).to.be.rejectedWith('Missing trailing signature packets');
        await expect(signatures[0].signature).to.be.rejectedWith('Missing trailing signature packets');
      });
      await openpgp.verify({ verificationKeys: [pubKey], message: messageWithoutGrammar, config: { minRSABits: 1024 } }).then(async ({ data, signatures }) => {
        expect(await stream.readToEnd(data)).to.equal(plaintext);
        expect(signatures).to.have.length(1);
        await expect(signatures[0].verified).to.be.rejectedWith('Corresponding signature packet missing');
        expect((await signatures[0].signature).packets.length).to.equal(0);
      });
    });
  }

  tests();

  let rejectMessageHashAlgorithms;
  tryTests('Accept SHA-1 signatures', tests, {
    if: true,
    before: function() {
      ({ rejectMessageHashAlgorithms } = openpgp.config);
      Object.assign(openpgp.config, { rejectMessageHashAlgorithms: new Set([openpgp.enums.hash.md5, openpgp.enums.hash.ripemd]) });
    },
    after: function() {
      Object.assign(openpgp.config, { rejectMessageHashAlgorithms });
    }
  });

  it('Sign text with openpgp.sign and verify with openpgp.verify leads to same string cleartext and valid signatures', async function() {
    const plaintext = 'short message\nnext line \n한국어/조선말';
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const privKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });

    const config = { minRSABits: 1024 };
    return openpgp.sign({ signingKeys: privKey, message: await openpgp.createCleartextMessage({ text: plaintext }), config }).then(async signed => {

      const message = await openpgp.readCleartextMessage({ cleartextMessage: signed });
      return openpgp.verify({ verificationKeys:[pubKey], message, config });

    }).then(async function({ data, signatures }) {
      expect(data).to.equal(plaintext.replace(/[ \t\r]+$/mg, ''));
      expect(signatures).to.have.length(1);
      expect(await signatures[0].verified).to.be.true;
      expect((await signatures[0].signature).packets.length).to.equal(1);
    });
  });

  it('Sign text with openpgp.sign and verify with openpgp.verify leads to same string cleartext and valid signatures -- escape armored message', async function() {
    const plaintext = pub_key_arm2;
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const privKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });

    const config = { minRSABits: 1024 };
    return openpgp.sign({ signingKeys: privKey, message: await openpgp.createCleartextMessage({ text: plaintext }), config }).then(async signed => {

      const message = await openpgp.readCleartextMessage({ cleartextMessage: signed });
      return openpgp.verify({ verificationKeys: pubKey, message, config });

    }).then(async function({ data, signatures }) {
      expect(data).to.equal(plaintext);
      expect(signatures).to.have.length(1);
      expect(await signatures[0].verified).to.be.true;
      expect((await signatures[0].signature).packets.length).to.equal(1);
    });
  });

  it('Sign text with openpgp.sign and verify with openpgp.verify leads to same string cleartext and valid signatures -- trailing spaces', async function() {
    const plaintext = 'space: \nspace and tab: \t\nno trailing space\n  \ntab:\t\ntab and space:\t ';
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const privKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });

    const config = { minRSABits: 1024 };
    return openpgp.sign({ signingKeys: privKey, message: await openpgp.createCleartextMessage({ text: plaintext }), config }).then(async signed => {

      const message = await openpgp.readCleartextMessage({ cleartextMessage: signed });
      return openpgp.verify({ verificationKeys: pubKey, message, config });

    }).then(async function({ data, signatures }) {
      expect(data).to.equal(plaintext.replace(/[ \t]+$/mg, ''));
      expect(signatures).to.have.length(1);
      expect(await signatures[0].verified).to.be.true;
      expect((await signatures[0].signature).packets.length).to.equal(1);
    });
  });

  it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - armored', async function() {
    const plaintext = util.stringToUint8Array('short message\nnext line \n한국어/조선말');
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const privKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });

    const config = { minRSABits: 1024 };
    return openpgp.sign({ signingKeys: privKey, message: await openpgp.createMessage({ binary: plaintext }), config }).then(async signed => {

      const message = await openpgp.readMessage({ armoredMessage: signed });
      return openpgp.verify({ verificationKeys: pubKey, message, format: 'binary', config });

    }).then(async function({ data, signatures }) {
      expect(data).to.deep.equal(plaintext);
      expect(signatures).to.have.length(1);
      expect(await signatures[0].verified).to.be.true;
      expect((await signatures[0].signature).packets.length).to.equal(1);
    });
  });

  it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - not armored', async function() {
    const plaintext = util.stringToUint8Array('short message\nnext line \n한국어/조선말');
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const privKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });

    const config = { minRSABits: 1024 };
    return openpgp.sign({ signingKeys: privKey, message: await openpgp.createMessage({ binary: plaintext }), format: 'binary', config }).then(async signed => {

      const message = await openpgp.readMessage({ binaryMessage: signed });
      return openpgp.verify({ verificationKeys: pubKey, message, format: 'binary', config });

    }).then(async function({ data, signatures }) {
      expect(data).to.deep.equal(plaintext);
      expect(signatures).to.have.length(1);
      expect(await signatures[0].verified).to.be.true;
      expect((await signatures[0].signature).packets.length).to.equal(1);
    });
  });

  it('Should verify detached signature with some unknown versions of Signature packets', async function () {
    // Test from openpgp-interoperability-test-suite to ensure forward compatibility: https://tests.sequoia-pgp.org/?q=forward-compat
    const plaintext = 'hello world';

    // This signature includes two Signature packets: a v4 one (verifiable) and a 'dummy' v23 one.
    const signatureUnknownTrailingPacketVersion = `-----BEGIN PGP SIGNATURE-----

wnUEARYKACcFgmSVpTQJkHEwNzxPuQajFiEE2KiARjeh+fU3dy+5cTA3PE+5
BqMAAKZNAP0fhECUqrE2Ts7Ho8/fuLFT+9jsGIGo0EviIEmW77vyhQEAtOBa
N77tTSawgDqnjIRH5RyI6YNC1LNz01VHCYWwegfCwTsXAAEKAG8FgmSVZN4J
EPv8yCoBXnMwRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdw
Lm9yZ8jF+epDaQ8yqg9h1mb0LcDLKC71kHyESC8fqFt9fNFsFiEE0aZuGiOx
gsmYD3iM+/zIKgFeczAAADLxDACKH0qwrZW+Eu3McHHfKojqlHoJ+Ofqotui
Gtcyx3HrE86xQHQl6346Joweomlzo2A6cjhT/nxL88sfy9yTQyUyKaON0wHz
4WI+Onu8rSaG99J/u34dDIPqFu5DzhwCrkv0IQwGYfDxG6Lrxg7gsxui2KAt
4rJqlbaeRGOTeNmew6aH74foUp86LWjdasanZ3RXxjk3yP+R/7nquQjkVGqE
jElkMwFh44TwTHlrXfI90Ki4gNrFQfbQCQm2v66rT0t3BSgVrL+FZIyXjjOh
dp83PCrkcvOcbBalvtbYPd5+23cGAylm5hkC9bxQUwUJrcJezdwSpxF5+Vgj
IkeanKfU2BhKry3Hpn3PL6vLfVkK/w0wUEbDMkFRbGAmW1sPCJWDSX6Zy75/
Li0CQ3u6tg3/m9VHUdwN5iNVk3g7AtV2eLinv4fKIuVUxUIyvacro+RBxGNc
EnZwTO2p2I0xifnoRizITFXclUc9J4vK+whpi9PHH5uoqRGcoer72rtjIIs=
=nReB
-----END PGP SIGNATURE-----`;

    const publicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----

xjMEZJWk4RYJKwYBBAHaRw8BAQdA7p5RuL+Z05qld6xRz6tbJ+9pmDowaCYr
tMOW8MXHAx3NFW5hbWUgPGVtYWlsQHRlc3QuY29tPsKMBBAWCgA+BYJklaTh
BAsJBwgJkHEwNzxPuQajAxUICgQWAAIBAhkBApsDAh4BFiEE2KiARjeh+fU3
dy+5cTA3PE+5BqMAAB5pAQDUHdYs3HRK6yJZ6IrK8lfmLzeqSgW2j9wLG/zF
TXIARQEAj0PdOzSy3q75VIQraDSHWpBAue8QNEKV4Q8hlkJvmgPOOARklaTh
EgorBgEEAZdVAQUBAQdAR9bBkzKzh24TB6gJVHR49BWnhTmeF5+vA3PXtX/b
RHkDAQgHwngEGBYIACoFgmSVpOEJkHEwNzxPuQajApsMFiEE2KiARjeh+fU3
dy+5cTA3PE+5BqMAAFjVAQDKqKwFLKX+N7le3cDLHAYSqc4AWpksKS4eSBLa
uDvEBgD+LCEUOPejUTCMqPyd04ssdOq1AlMJOmUGUwLk7kFP7Aw=
=Q9Px
-----END PGP PUBLIC KEY BLOCK-----`;

    const { signatures, data } = await openpgp.verify({
      message: await openpgp.createMessage({ text: plaintext }),
      signature: await openpgp.readSignature({ armoredSignature: signatureUnknownTrailingPacketVersion }),
      verificationKeys: await openpgp.readKey({ armoredKey: publicKey })
    });
    expect(data).to.equal(plaintext);
    expect(signatures).to.have.length(1);
    expect(await signatures[0].verified).to.be.true;
    expect((await signatures[0].signature).packets.length).to.equal(1);
  });

  it('Should verify cleartext signature with some unknown versions of Signature packets', async function () {
    // Test to ensure forward compatibility:
    // this signature includes two Signature packets: a v4 one (verifiable) and a 'dummy' v23 one.
    const signatureUnknownTrailingPacketVersion = `-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

hello world
-----BEGIN PGP SIGNATURE-----

wnUEARYKACcFgmSVpTQJkHEwNzxPuQajFiEE2KiARjeh+fU3dy+5cTA3PE+5
BqMAAKZNAP0fhECUqrE2Ts7Ho8/fuLFT+9jsGIGo0EviIEmW77vyhQEAtOBa
N77tTSawgDqnjIRH5RyI6YNC1LNz01VHCYWwegfCwTsXAAEKAG8FgmSVZN4J
EPv8yCoBXnMwRxQAAAAAAB4AIHNhbHRAbm90YXRpb25zLnNlcXVvaWEtcGdw
Lm9yZ8jF+epDaQ8yqg9h1mb0LcDLKC71kHyESC8fqFt9fNFsFiEE0aZuGiOx
gsmYD3iM+/zIKgFeczAAADLxDACKH0qwrZW+Eu3McHHfKojqlHoJ+Ofqotui
Gtcyx3HrE86xQHQl6346Joweomlzo2A6cjhT/nxL88sfy9yTQyUyKaON0wHz
4WI+Onu8rSaG99J/u34dDIPqFu5DzhwCrkv0IQwGYfDxG6Lrxg7gsxui2KAt
4rJqlbaeRGOTeNmew6aH74foUp86LWjdasanZ3RXxjk3yP+R/7nquQjkVGqE
jElkMwFh44TwTHlrXfI90Ki4gNrFQfbQCQm2v66rT0t3BSgVrL+FZIyXjjOh
dp83PCrkcvOcbBalvtbYPd5+23cGAylm5hkC9bxQUwUJrcJezdwSpxF5+Vgj
IkeanKfU2BhKry3Hpn3PL6vLfVkK/w0wUEbDMkFRbGAmW1sPCJWDSX6Zy75/
Li0CQ3u6tg3/m9VHUdwN5iNVk3g7AtV2eLinv4fKIuVUxUIyvacro+RBxGNc
EnZwTO2p2I0xifnoRizITFXclUc9J4vK+whpi9PHH5uoqRGcoer72rtjIIs=
=nReB
-----END PGP SIGNATURE-----`;

    const publicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----

xjMEZJWk4RYJKwYBBAHaRw8BAQdA7p5RuL+Z05qld6xRz6tbJ+9pmDowaCYr
tMOW8MXHAx3NFW5hbWUgPGVtYWlsQHRlc3QuY29tPsKMBBAWCgA+BYJklaTh
BAsJBwgJkHEwNzxPuQajAxUICgQWAAIBAhkBApsDAh4BFiEE2KiARjeh+fU3
dy+5cTA3PE+5BqMAAB5pAQDUHdYs3HRK6yJZ6IrK8lfmLzeqSgW2j9wLG/zF
TXIARQEAj0PdOzSy3q75VIQraDSHWpBAue8QNEKV4Q8hlkJvmgPOOARklaTh
EgorBgEEAZdVAQUBAQdAR9bBkzKzh24TB6gJVHR49BWnhTmeF5+vA3PXtX/b
RHkDAQgHwngEGBYIACoFgmSVpOEJkHEwNzxPuQajApsMFiEE2KiARjeh+fU3
dy+5cTA3PE+5BqMAAFjVAQDKqKwFLKX+N7le3cDLHAYSqc4AWpksKS4eSBLa
uDvEBgD+LCEUOPejUTCMqPyd04ssdOq1AlMJOmUGUwLk7kFP7Aw=
=Q9Px
-----END PGP PUBLIC KEY BLOCK-----`;

    const { signatures } = await openpgp.verify({
      message: await openpgp.readCleartextMessage({ cleartextMessage: signatureUnknownTrailingPacketVersion }),
      verificationKeys: await openpgp.readKey({ armoredKey: publicKey })
    });
    expect(signatures).to.have.length(1);
    expect(await signatures[0].verified).to.be.true;
    expect((await signatures[0].signature).packets.length).to.equal(1);
  });

  it('Should verify cleartext message correctly when using a detached cleartext signature and binary literal data', async function () {
    const plaintext = 'short message\nnext line \n한국어/조선말';
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const privKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });

    const config = { minRSABits: 1024 };
    return openpgp.sign({ signingKeys: privKey, message: await openpgp.createMessage({ text: plaintext }), detached: true, config }).then(async armoredSignature => {
      const signature = await openpgp.readSignature({ armoredSignature });
      return openpgp.verify({ verificationKeys: pubKey, message: await openpgp.createMessage({ binary: util.encodeUTF8(plaintext) }), signature, config });
    }).then(async function({ data, signatures }) {
      expect(data).to.equal(plaintext);
      expect(signatures).to.have.length(1);
      expect(await signatures[0].verified).to.be.true;
      expect((await signatures[0].signature).packets.length).to.equal(1);
    });
  });

  it('Should verify cleartext message correctly when using a detached binary signature and text literal data', async function () {
    const plaintext = 'short message\nnext line \n한국어/조선말';
    const binaryPlaintext = util.encodeUTF8(plaintext);
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const privKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });

    const config = { minRSABits: 1024 };
    return openpgp.sign({ signingKeys: privKey, message:await openpgp.createMessage({ binary: binaryPlaintext }), detached: true, config }).then(async armoredSignature => {
      const signature = await openpgp.readSignature({ armoredSignature });
      return openpgp.verify({ verificationKeys: pubKey, message: await openpgp.createMessage({ text: plaintext }), signature, config });

    }).then(async function({ data, signatures }) {
      expect(data).to.equal(plaintext);
      expect(signatures).to.have.length(1);
      expect(await signatures[0].verified).to.be.true;
      expect((await signatures[0].signature).packets.length).to.equal(1);
    });
  });

  it('Should verify encrypted cleartext message correctly when encrypting binary literal data with a canonical text signature', async function () {
    const plaintext = 'short message\nnext line \n한국어/조선말';
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const privKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });

    const config = { minRSABits: 1024 };
    return openpgp.sign({ signingKeys: privKey, message: await openpgp.createMessage({ text: plaintext }), detached: true, config }).then(async armoredSignature => {
      const signature = await openpgp.readSignature({ armoredSignature });
      return openpgp.encrypt({ message: await openpgp.createMessage({ binary: util.encodeUTF8(plaintext) }), encryptionKeys: [pubKey], signature, config });

    }).then(async armoredMessage => {
      const message = await openpgp.readMessage({ armoredMessage });
      return openpgp.decrypt({ message, decryptionKeys: [privKey], verificationKeys: [pubKey], config });

    }).then(async function({ data, signatures }) {
      expect(data).to.equal(plaintext);
      expect(signatures).to.have.length(1);
      expect(await signatures[0].verified).to.be.true;
      expect((await signatures[0].signature).packets.length).to.equal(1);
    });
  });

  // TODO add test with multiple revocation signatures
  it('Verify primary key revocation signatures', async function() {
    const pubKey = await openpgp.readKey({ armoredKey: pub_revoked });
    await pubKey.revocationSignatures[0].verify(
      pubKey.keyPacket, openpgp.enums.signature.keyRevocation, { key: pubKey.keyPacket }
    );
  });

  // TODO add test with multiple revocation signatures
  it('Verify subkey revocation signatures', async function() {
    const pubKey = await openpgp.readKey({ armoredKey: pub_revoked });
    const revSig = pubKey.subkeys[0].revocationSignatures[0];
    await revSig.verify(
      pubKey.keyPacket, openpgp.enums.signature.subkeyRevocation, { key: pubKey.keyPacket, bind: pubKey.subkeys[0].keyPacket }
    );
  });

  it('Verify key expiration date', async function() {
    const pubKey = await openpgp.readKey({ armoredKey: pub_revoked });

    expect(pubKey).to.exist;
    expect(pubKey.users[0].selfCertifications[0].keyNeverExpires).to.be.false;
    expect(pubKey.users[0].selfCertifications[0].keyExpirationTime).to.equal(5 * 365 * 24 * 60 * 60);
  });

  it('Write unhashed subpackets', async function() {
    let pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    expect(pubKey.users[0].selfCertifications).to.exist;
    pubKey = await openpgp.readKey({ armoredKey: pubKey.armor() });
    expect(pubKey.users[0].selfCertifications).to.exist;
  });

  it('Write V4 signatures', async function() {
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const pubKey2 = await openpgp.readKey({ armoredKey: pubKey.armor() });
    expect(pubKey2).to.exist;
    expect(pubKey.users[0].selfCertifications).to.eql(pubKey2.users[0].selfCertifications);
  });

  it('Verify a detached signature using appendSignature', async function() {
    const detachedSig = ['-----BEGIN PGP SIGNATURE-----',
      'Version: GnuPG v1.4.13 (Darwin)',
      'Comment: GPGTools - https://gpgtools.org',
      'Comment: Using GnuPG with Thunderbird - https://www.enigmail.net/',
      '',
      'iQEcBAEBCgAGBQJTqH5OAAoJENf7k/zfv8I8oFoH/R6EFTw2CYUQoOKSAQstWIHp',
      'fVVseLOkFbByUV5eLuGVBNI3DM4GQ6C7dGntKAn34a1iTGcAIZH+fIhaZ2WtNdtA',
      'R+Ijn8xDjbF/BWvcTBOaRvgw9b8viPxhkVYa3PioHYz6krt/LmFqFdp/phWZcqR4',
      'jzWMX55h4FOw3YBNGiz2NuIg+iGrFRWPYgd8NVUmJKReZHs8C/6HGz7F4/A24k6Y',
      '7xms9D6Er+MhspSl+1dlRdHjtXiRqC5Ld1hi2KBKc6YzgOLpVw5l9sffbnH+aRG4',
      'dH+2J5U3elqBDK1i3GyG8ixLSB0FGW9+lhYNosZne2xy8SbQKdgsnTBnWSGevP0=',
      '=xiih',
      '-----END PGP SIGNATURE-----'].join('\r\n');

    const content = ['Content-Type: multipart/mixed;',
      ' boundary="------------070307080002050009010403"',
      '',
      'This is a multi-part message in MIME format.',
      '--------------070307080002050009010403',
      'Content-Type: text/plain; charset=ISO-8859-1',
      'Content-Transfer-Encoding: quoted-printable',
      '',
      'test11',
      '',
      '--------------070307080002050009010403',
      'Content-Type: application/macbinary;',
      ' name="test.bin"',
      'Content-Transfer-Encoding: base64',
      'Content-Disposition: attachment;',
      ' filename="test.bin"',
      '',
      'dGVzdGF0dGFjaG1lbnQ=',
      '--------------070307080002050009010403--',
      ''].join('\r\n');

    const publicKeyArmored = '-----BEGIN PGP PUBLIC KEY BLOCK-----\r\nVersion: OpenPGP.js v.1.20131116\r\nComment: Whiteout Mail - https://whiteout.io\r\n\r\nxsBNBFKODs4BB/9iOF4THsjQMY+WEpT7ShgKxj4bHzRRaQkqczS4nZvP0U3g\r\nqeqCnbpagyeKXA+bhWFQW4GmXtgAoeD5PXs6AZYrw3tWNxLKu2Oe6Tp9K/XI\r\nxTMQ2wl4qZKDXHvuPsJ7cmgaWqpPyXtxA4zHHS3WrkI/6VzHAcI/y6x4szSB\r\nKgSuhI3hjh3s7TybUC1U6AfoQGx/S7e3WwlCOrK8GTClirN/2mCPRC5wuIft\r\nnkoMfA6jK8d2OPrJ63shy5cgwHOjQg/xuk46dNS7tkvGmbaa+X0PgqSKB+Hf\r\nYPPNS/ylg911DH9qa8BqYU2QpNh9jUKXSF+HbaOM+plWkCSAL7czV+R3ABEB\r\nAAHNLVdoaXRlb3V0IFVzZXIgPHNhZmV3aXRobWUudGVzdHVzZXJAZ21haWwu\r\nY29tPsLAXAQQAQgAEAUCUo4O2gkQ1/uT/N+/wjwAAN2cB/9gFRmAfvEQ2qz+\r\nWubmT2EsSSnjPMxzG4uyykFoa+TaZCWo2Xa2tQghmU103kEkQb1OEjRjpgwJ\r\nYX9Kghnl8DByM686L5AXnRyHP78qRJCLXSXl0AGicboUDp5sovaa4rswQceH\r\nvcdWgZ/mgHTRoiQeJddy9k+H6MPFiyFaVcFwegVsmpc+dCcC8yT+qh8ZIbyG\r\nRJU60PmKKN7LUusP+8DbSv39zCGJCBlVVKyA4MzdF5uM+sqTdXbKzOrT5DGd\r\nCZaox4s+w16Sq1rHzZKFWfQPfKLDB9pyA0ufCVRA3AF6BUi7G3ZqhZiHNhMP\r\nNvE45V/hS1PbZcfPVoUjE2qc1Ix1\r\n=7Wpe\r\n-----END PGP PUBLIC KEY BLOCK-----';
    const publicKey = await openpgp.readKey({ armoredKey: publicKeyArmored });

    const message = await openpgp.createMessage({ text: content });
    await message.appendSignature(detachedSig);
    const { data, signatures } = await openpgp.verify({ verificationKeys:[publicKey], message, config: { minRSABits: 1024, allowMissingKeyFlags: true } });
    expect(data).to.equal(content);
    expect(signatures).to.have.length(1);
    expect(await signatures[0].verified).to.be.true;
    expect((await signatures[0].signature).packets.length).to.equal(1);
    expect(await signatures[0].verified).to.be.true;
  });

  it('Detached signature signing and verification', async function() {
    const message = await openpgp.createMessage({ text: 'hello' });
    const pubKey = await openpgp.readKey({ armoredKey: pub_key_arm2 });
    const privKey = await openpgp.decryptKey({
      privateKey: await openpgp.readKey({ armoredKey: priv_key_arm2 }),
      passphrase: 'hello world'
    });

    const opt = { userIDs: { name:'test', email:'a@b.com' }, format: 'object' };
    const { privateKey: generatedKey } = await openpgp.generateKey(opt);
    const armoredSignature = await openpgp.sign({ signingKeys: [generatedKey, privKey], message, detached: true, config: { minRSABits: 1024 } });
    const signature = await openpgp.readSignature({ armoredSignature });
    const { data, signatures } = await openpgp.verify({ verificationKeys: [generatedKey.toPublic(), pubKey], message, signature, config: { minRSABits: 1024 } });
    expect(data).to.equal('hello');
    expect(await signatures[0].verified).to.be.true;
    expect(await signatures[1].verified).to.be.true;
  });

  it('Sign message with key without password', function() {
    const opt = { userIDs: { name:'test', email:'a@b.com' }, passphrase: null, format: 'object' };
    return openpgp.generateKey(opt).then(async function({ privateKey: key }) {
      const message = await openpgp.createMessage({ text: 'hello world' });
      return message.sign([key]);
    });
  });

  it('Verify signed key', async function() {
    const signedArmor = [
      '-----BEGIN PGP PUBLIC KEY BLOCK-----',
      'Version: GnuPG v1',
      '',
      'mI0EUmEvTgEEANyWtQQMOybQ9JltDqmaX0WnNPJeLILIM36sw6zL0nfTQ5zXSS3+',
      'fIF6P29lJFxpblWk02PSID5zX/DYU9/zjM2xPO8Oa4xo0cVTOTLj++Ri5mtr//f5',
      'GLsIXxFrBJhD/ghFsL3Op0GXOeLJ9A5bsOn8th7x6JucNKuaRB6bQbSPABEBAAG0',
      'JFRlc3QgTWNUZXN0aW5ndG9uIDx0ZXN0QGV4YW1wbGUuY29tPoi5BBMBAgAjBQJS',
      'YS9OAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQSmNhOk1uQJQwDAP6',
      'AgrTyqkRlJVqz2pb46TfbDM2TDF7o9CBnBzIGoxBhlRwpqALz7z2kxBDmwpQa+ki',
      'Bq3jZN/UosY9y8bhwMAlnrDY9jP1gdCo+H0sD48CdXybblNwaYpwqC8VSpDdTndf',
      '9j2wE/weihGp/DAdy/2kyBCaiOY1sjhUfJ1GogF49rCIRgQQEQIABgUCVuXBfQAK',
      'CRARJ5QDyxae+O0fAJ9hUQPejXvZv6VW1Q3/Pm3+x2wfJACgwFg9NlrPPfejoC1w',
      'P+z+vE5NFA24jQRSYS9OAQQA6R/PtBFaJaT4jq10yqASk4sqwVMsc6HcifM5lSdx',
      'zExFP74naUMMyEsKHP53QxTF0GrqusagQg/ZtgT0CN1HUM152y7ACOdp1giKjpMz',
      'OTQClqCoclyvWOFB+L/SwGEIJf7LSCErwoBuJifJc8xAVr0XX0JthoW+uP91eTQ3',
      'XpsAEQEAAYkBPQQYAQIACQUCUmEvTgIbLgCoCRBKY2E6TW5AlJ0gBBkBAgAGBQJS',
      'YS9OAAoJEOCE90RsICyXuqIEANmmiRCASF7YK7PvFkieJNwzeK0V3F2lGX+uu6Y3',
      'Q/Zxdtwc4xR+me/CSBmsURyXTO29OWhPGLszPH9zSJU9BdDi6v0yNprmFPX/1Ng0',
      'Abn/sCkwetvjxC1YIvTLFwtUL/7v6NS2bZpsUxRTg9+cSrMWWSNjiY9qUKajm1tu',
      'zPDZXAUEAMNmAN3xXN/Kjyvj2OK2ck0XW748sl/tc3qiKPMJ+0AkMF7Pjhmh9nxq',
      'E9+QCEl7qinFqqBLjuzgUhBU4QlwX1GDAtNTq6ihLMD5v1d82ZC7tNatdlDMGWnI',
      'dvEMCv2GZcuIqDQ9rXWs49e7tq1NncLYhz3tYjKhoFTKEIq3y3Pp',
      '=fvK7',
      '-----END PGP PUBLIC KEY BLOCK-----'
    ].join('\n');

    const signedKey = await openpgp.readKey({ armoredKey: signedArmor });
    const signerKey = await openpgp.readKey({ armoredKey: priv_key_arm1 });
    return signedKey.verifyPrimaryUser([signerKey], undefined, undefined, { ...openpgp.config, rejectPublicKeyAlgorithms: new Set() }).then(signatures => {
      expect(signatures[0].valid).to.be.null;
      expect(signatures[0].keyID.toHex()).to.equal(signedKey.getKeyID().toHex());
      expect(signatures[1].valid).to.be.true;
      expect(signatures[1].keyID.toHex()).to.equal(signerKey.getKeyID().toHex());
    });
  });

  it('Verify signed UserIDs and User Attributes', async function() {
    const armoredKeyWithPhoto = `-----BEGIN PGP PUBLIC KEY BLOCK-----

mI0EW1CJGAEEAM+BzuFzcYk9HttmDbjGexQ8dfme074Q5PuHas3PBISPm0AwmnDM
tzjlcrrg2VGuLqHvNF600w2ZgOo2gElNYCOas1q/fVFuIgJ4SUduNOEe/JnIW4uP
iEGU9l6zOVVgTc/nGVpZdvHgvOL8nl9BKHtWEnMD3Du7UYAm+Avshu9jABEBAAG0
AViI1AQTAQoAPhYhBKcH118Rrg0wLBrTk5IyMikCym+4BQJbUIkYAhsDBQkDwmcA
BQsJCAcDBRUKCQgLBRYDAgEAAh4BAheAAAoJEJIyMikCym+4K8oEAJc7YFiNau6V
HTVK4cTvWU5MuYiejejFZai4ELUJy+WF6cZYrLuF/z/kRt8B7hpumXChPCUlT0q7
FWypQtA3leu83DGMXqhfS80h2S1+VLmDVVWKQXOwgOb44jT9F08bDU5QK08SkjF8
/EirIy8ANzdwCA4rHytIS2yx6tLlthvX0cBwwG4BEAABAQAAAAAAAAAAAAAAAP/Y
/+AAEEpGSUYAAQEAAAEAAQAA/9sAQwABAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB/9sAQwEBAQEB
AQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEB
AQEBAQEBAQEBAQEB/8AAEQgAAQABAwEiAAIRAQMRAf/EABUAAQEAAAAAAAAAAAAA
AAAAAAAK/8QAFBABAAAAAAAAAAAAAAAAAAAAAP/EABQBAQAAAAAAAAAAAAAAAAAA
AAD/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwC/gAH/2YjUBBMB
CgA+FiEEpwfXXxGuDTAsGtOTkjIyKQLKb7gFAltQimUCGwMFCQPCZwAFCwkIBwMF
FQoJCAsFFgMCAQACHgECF4AACgkQkjIyKQLKb7gm/wQAiyZF89qr8hf3XQNJ6Ir/
QtaniPcesjrYCIE47ZfeDYpBTPeiMm295o2dZXVJS4ItllYsplASw5DJiIMnQKlJ
mbXakYFzzclTa/JrKzFYCy/DPT95xK+633omgrIUgJodizoKJE7XeB2U6aRUJJ4O
iTuGu4fEU1UligAXSrZmCdE=
=VK6I
-----END PGP PUBLIC KEY BLOCK-----`;

    const key = await openpgp.readKey({ armoredKey: armoredKeyWithPhoto });
    await Promise.all(key.users.map(async user => {
      await user.verify(undefined, openpgp.config);
    }));
  });

  it('should verify a shorter RSA signature', async function () {
    const encrypted = `-----BEGIN PGP MESSAGE-----

wYwD4IT3RGwgLJcBBACmH+a2c2yieZJ3wFchKeTVqzWkoltiidWgHHNE5v5x
8aZGNzZFBd02v80VS23P9oxeJOpqKX2IZyuD36SniNoi+eXdT3zraqIe9x5p
0RY9OrTP9pl58iogFBi1ARls41j7ui8KKDt2/iyQDCWHW1LoOVstiEb5/Xi3
EWI+34EbNNTBMgEJAQAwEXImkOPmhYhE7bB3FnXe9rb7Fo3GZYA4/8B9YVf7
GGZRLGwbICGu8E0MolmzLYW9hRThEfusAsNPGSgB+Yaqp0drsk01N4JJj3FT
RKEUvd5EcL3u+Z5EoUUW6GpUL5p8Hvy2thqQfeem7XUbDBY6V3wqydOjbN9u
c4CWB5Zu3GjDGDOvXFsy6cgdQvd/B9xbugKvUbAIsecTPlLtjZwfQklIu63T
DA/3Pz/+zTAknBCsuIM0m7U/ZP3N6AGQIp4To7RJk0I6AxthHF5LbU11MjDZ
iB7+vmhqlrPyIS11g25UNijottJm13f84glVwBdWTJCiEqjh3KbcnTQCckCY
V39DDLtbZG/XIx1ktqp765O9D/9xp2IA4zTyZzH4TuDbYs1j+JRdMsAq254k
1m+wtW5gxJGcD5nh2T2T+ABL0n3jW0G504kR0LNBAQOZhVSKnSLn+F0GkjmI
iGw8+BOy8p2pX/WCLOf776ppSL77TpzhpG6wSE2oQxDrudazmRgVkZpyGzFE
fDjspLTJHOhZ5zlLuoiKS9qEARGp39ysQnElR4dsx7tyVZz0uJvIrVzrQBlB
ekoD0DH0bhfqiwDrqeTJT2ORk8I/Q3jWnhQ3MnRN+q9d0yf1LWApMCwA7xU2
C4KUFRC/wuF2TR9NvA==
=v3WS
-----END PGP MESSAGE-----`;
    const armoredKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----

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
oaBUyhCKt8tz6Q==
=52k1
-----END PGP PRIVATE KEY BLOCK-----`;
    const key = await openpgp.readKey({ armoredKey });
    const decrypted = await openpgp.decrypt({
      message: await openpgp.readMessage({ armoredMessage: encrypted, config: { enableParsingV5Entities: true } }),
      verificationKeys: key,
      decryptionKeys: key,
      config: { minRSABits: 1024 }
    });
    expect(await decrypted.signatures[0].verified).to.be.true;
  });

  it('should verify a shorter EdDSA signature', async function() {
    const key = await openpgp.readKey({
      armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xVgEX8+jfBYJKwYBBAHaRw8BAQdA9GbdDjprR0sWf0R5a5IpulUauc0FsmzJ
mOYCfoowt8EAAP9UwaqC0LWWQ5RlX7mps3728vFa/If1KBVwAjk7Uqhi2BKL
zQ90ZXN0MiA8YkBhLmNvbT7CjAQQFgoAHQUCX8+jfAQLCQcIAxUICgQWAgEA
AhkBAhsDAh4BACEJEG464aV2od77FiEEIcg441MtKnyJnPDRbjrhpXah3vuR
gQD+Il6Gw2oIok4/ANyDDLBYZtKqRrMv4NcfF9DHYuAFcP4BAPhFOffyP3qU
AEZb7QPrWdLfhn8/FeSFZxJvnmupQ9sDx10EX8+jfBIKKwYBBAGXVQEFAQEH
QOSzo9cX1U2esGFClprOt0QWXNJ97228R5tKFxo6/0NoAwEIBwAA/0n4sq2i
N6/jE+6rVO4o/7LW0xahxpV1tTA6qv1Op9TwFIDCeAQYFggACQUCX8+jfAIb
DAAhCRBuOuGldqHe+xYhBCHIOONTLSp8iZzw0W464aV2od773XcA/jlmX8/c
1/zIotEkyMZB4mI+GAg3FQ6bIACFBH1sz0MzAP9Snri0P4FRZ8D5THRCJoUm
GBgpBmrf6IVv484jBswGDA==
=8rBO
-----END PGP PRIVATE KEY BLOCK-----`
    });
    const encrypted = `-----BEGIN PGP MESSAGE-----

wV4DWlRRjuYiLSsSAQdAWwDKQLN4ZUS5fqiwFtAMrRfZZe9J4SgClhG6avEe
AEowkSZwWRT+8Hy8aBIb4oPehYUFXXZ7BtlJCyd7LOTUtqyc00OE0721PC3M
v0+zird60sACATlDmTwweR5GFtEAjHVheIL5rbkOBRD+oSqB8z+IovNg83Pz
FVwsFZnCLtECoYgpF2MJdopuC/bPHcrvf4ndwmD11uXtms4Rq4y25QyqApbn
Hj/hljufk0OkavUXxrNKjGQtxLHMpa3Nsi0MHWY8JguxOKFKpAIMP32CD1e+
j+GItrR+QbbN13ODlcR3hf66cwjLLsJCx5VcBaRspKF05O3ix/u9KVjJqtbi
Ie6jnY0zP2ldtS4JmhKBa43qmOHCxHc=
=7B58
-----END PGP MESSAGE-----`;
    const decrypted = await openpgp.decrypt({ message: await openpgp.readMessage({ armoredMessage: encrypted }), decryptionKeys: key, verificationKeys: key.toPublic() });
    expect(await decrypted.signatures[0].verified).to.be.true;
  });

  it('should verify a shorter ECDSA signature', async function() {
    const key = await openpgp.readKey({
      armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----

xYAFX9JrLRMAAABMCCqGSM49AwEHAgMErtQdX4vh7ng/ut+k1mooYNh3Ywqt
wr0tSS8hxZMvQRIFQ53Weq0e97ioZKXGimprEL571yvAN7I19wtQtqi61AAA
AAAAJAEAjWdW+qlMFaKwXCls3O/X8I1rbZ0OdFgeE3TnRP3YETAP5s0KYSA8
YUBhLml0PsKSBRATCAAhBQJf0mstBAsJBwgDFQgKBBYCAQACGQECGwMCHgcD
IgECACMiIQUee6Tb+GlhTk/ozKrt7RhInCyR6w3OJb/tYAN1+qbIoYUqAP9S
XmJCmSMrq6KfAD1aWSTBhtmujh+6y/pYTaf6VJVBYQEAt18zK0tw5EihHASY
FXbfdFHBzrMmPJ4UV6UiBvH6k2zHhAVf0mstEgAAAFAIKoZIzj0DAQcCAwQx
qnVPmWex365Nx8X8BGuMNI2TITXzTh9+AuPftZjPm09dhxdT9xmrCstPu/U1
cpacIp0LIq13ngLgeZWcGFcnAwEIBwAAAAAAJAEAsTvBsKk/XoCz2mi8sz5q
EYaN9YdDOU2jF+HOaSNaJAsPF8J6BRgTCAAJBQJf0mstAhsMACMiIQUee6Tb
+GlhTk/ozKrt7RhInCyR6w3OJb/tYAN1+qbIoVutAP9GHPLn7D9Uahm81lhK
AcvDfr9a0Cp4WAVzKDKLUzrRMgEAozi0VyjiBo1U2LcwTPJkA4PEQqQRVW1D
KZTMSAH7JEo=
=tqWy
-----END PGP PRIVATE KEY BLOCK-----`,
      config: { enableParsingV5Entities: true }
    });
    const signed = `-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

short message
-----BEGIN PGP SIGNATURE-----

wnYFARMIAAYFAl/Say0AIyIhBR57pNv4aWFOT+jMqu3tGEicLJHrDc4lv+1g
A3X6psihFkcA+Nuog2qpAq20Zc2lzVjDZzQosb8MLvKMg3UFCX12Oc0BAJwd
JImeZLY02MctIpGZULbqgcUGK0P/yqrPL8Pe4lQM
=Pacb
-----END PGP SIGNATURE-----`;
    const message = await openpgp.readCleartextMessage({ cleartextMessage: signed, config: { enableParsingV5Entities: true } });
    const verified = await openpgp.verify({ verificationKeys: key, message });
    expect(await verified.signatures[0].verified).to.be.true;
  });

  it('Should parse a signature with a critical unknown subpacket, but not verify it', async function() {
    const key = await openpgp.readKey({
      armoredKey: `-----BEGIN PGP PUBLIC KEY BLOCK-----

xjMEZmsxYRYJKwYBBAHaRw8BAQdAgPH3tbfVO4CNqRQevvYW6kYY0qpNQltw
CegLonECw/vNBFRlc3TCwBgEEBYKAIoFgmZrMWEDCwkHCZAFbxb2+9/G3UUU
AAAAAAAcACBzYWx0QG5vdGF0aW9ucy5vcGVucGdwanMub3Jn1Bg/fpBZjM6n
CMTgcCh7+NHCoTmgpPef1+7CO792jL4FFQgKDA4EFgACAQIZAQKbAwIeARYh
BL/u0Jl6QJQVEZ0grQVvFvb738bdBOMBAgMAAMAYAQD25k4by+9P5WuOvirp
MhKE441PBb1n3fhaVpLogoVgZwD/ST2+Y5G6NdJM+U45iwfZDfa3ix1/zUSf
DF+cVdXVOwrOOARmazFhEgorBgEEAZdVAQUBAQdAGVw9vpajNPafAzshTmok
O1ZCDuQN9KkV+qTxZ7JGoEIDAQgHwsADBBgWCgB1BYJmazFhCZAFbxb2+9/G
3UUUAAAAAAAcACBzYWx0QG5vdGF0aW9ucy5vcGVucGdwanMub3JnRIP2KWB1
C8+8vpmscsPPBl+KYeNcCbCOJqo7G3A5ES0CmwwWIQS/7tCZekCUFRGdIK0F
bxb2+9/G3QTjAQIDAABj9wEA2E/C98UXszf4TWH7/xBGICoDDNxceMhSDvtt
nYhoNlUA/Ar+Ofx+vMf9oYcNjPEbYu/yu1AtKY44aZvDBLK2+OAI
=YrJy
-----END PGP PUBLIC KEY BLOCK-----`
    });
    await expect(key.verifyPrimaryKey()).to.be.rejectedWith(/Unknown critical signature subpacket type 99/);
  });
});
